tello_edu/
tello.rs

1use tokio::net::UdpSocket;
2use tokio::time::{sleep, Duration};
3use tokio::sync::Mutex;
4
5use crate::errors::{Result, TelloError};
6use crate::wifi::wait_for_wifi;
7use crate::state::*;
8use crate::video::*;
9use crate::command::*;
10use crate::options::TelloOptions;
11
12const DEFAULT_DRONE_HOST:&str = "192.168.10.1";
13
14const CONTROL_UDP_PORT:i32 = 8889;
15
16/// Initial state - no WiFi network
17#[derive(Debug)]
18pub struct NoWifi;
19
20/// The drone WiFi has been joined, but no UDP messages have been sent or received.
21#[derive(Debug)]
22pub struct Disconnected;
23
24/// The connection exchange has been completed and the drone is ready to fly.
25#[derive(Debug)]
26pub struct Connected {
27    sock: UdpSocket,
28    state_listener: Option<StateListener>,
29    video_listener: Option<VideoListener>,
30    command_receiver: Option<Mutex<TelloCommandReceiver>>
31}
32
33/// For interacting with the Tello EDU drone using the simple text-based UDP protocol.
34///
35/// The basic flow from the user's point of view is
36///
37///    SEND `command` → drone does something → RECEIVE `response` when it's finished
38///
39/// Messages are plain ASCII text, eg command `forward 10` → response `ok`
40///
41/// ```
42/// use tello_edu::{Tello, Result};
43/// 
44/// #[tokio::main]
45/// async fn main() {
46///     fly().await.unwrap();
47/// }
48/// 
49/// async fn fly() -> Result<()> {
50///     // create a new drone in the `NoWifi` state     
51///     let drone = Tello::new();
52///
53///     // wait until the host computer joins the drone's WiFi network
54///     // (joining the network is not automatic - how it happens is up to you)
55///     let drone = drone.wait_for_wifi().await?;
56/// 
57///     // establish connection and put the drone in "command" mode
58///     let drone = drone.connect().await?;
59/// 
60///     // fly!
61///     drone.take_off().await?;
62///     drone.turn_clockwise(360).await?;
63///     drone.land().await?;
64/// 
65///     Ok(())
66/// }
67/// ```
68#[derive(Debug)]
69pub struct Tello<S = NoWifi> {
70    /// The connection state of the drone.
71    inner: S
72}
73
74impl Tello<NoWifi> {
75    /// Create a new drone in a completely unconnected state.
76    pub fn new() -> Self {
77        Self { inner: NoWifi }
78    }
79
80    /// Wait until the host joins the drone's WiFi network
81    ///
82    /// *nb* exactly how the the network is joined is up to you
83    ///
84    pub async fn wait_for_wifi(&self) -> Result<Tello<Disconnected>>  {
85        println!("[Tello] waiting for WiFi...");
86        wait_for_wifi("TELLO").await?;
87        Ok(Tello { inner: Disconnected })
88    }
89
90    /// Use this if you are already in the appropriate WiFi network. 
91    pub async fn assume_wifi(&self) -> Result<Tello<Disconnected>>  {
92        println!("[Tello] assuming WiFi has already been joined");
93        Ok(Tello { inner: Disconnected })
94    }    
95}
96
97impl Tello<Disconnected> {
98    /// Connect to the drone using the default options, ie
99    /// - using the drone's own WiFi
100    /// - drone address 192.168.10.1
101    /// - no state updates
102    /// - no video
103    pub async fn connect(&self) -> Result<Tello<Connected>> {
104        self.connect_with(TelloOptions::default()).await
105    }
106
107    /// Connect to the drone using the given options
108    ///
109    /// - `options` Connection options
110    ///
111    pub async fn connect_with(&self, options:TelloOptions) -> Result<Tello<Connected>> {
112        let local_address = format!("0.0.0.0:{CONTROL_UDP_PORT}");
113
114        let drone_host = DEFAULT_DRONE_HOST;
115        let drone_address = format!("{drone_host}:{CONTROL_UDP_PORT}");
116
117        println!("[Tello] CONNECT {local_address} → {drone_address}");
118
119        // bind local socket
120        println!("[Tello] binding local {local_address}...");
121        let sock = UdpSocket::bind(&local_address).await?;
122        
123        // connect to drone
124        println!("[Tello] connecting to drone at {drone_address}...");
125        let mut i = 0;
126        loop {
127            i = i + 1;
128            match sock.connect(&drone_address).await {
129                Ok(_) => {
130                    println!("[Tello] CONNECTED");
131                    break;
132                }
133                Err(err) => {
134                    println!("[Tello] connection attempt #{i} failed ({err}), retrying...");
135                    sleep(Duration::from_millis(100)).await;
136                }
137            }
138        }
139
140        // connected drone, control only
141        let mut drone = Tello { inner: Connected { sock, state_listener: None, video_listener: None, command_receiver: None } };
142
143        // want drone state?
144        if let Some(state_tx) = &options.state_sender {
145            let state_listener = StateListener::start_listening(state_tx.clone()).await?;
146            drone.inner.state_listener = Some(state_listener);
147        }
148
149        // want drone video?
150        if let Some(video_tx) = &options.video_sender {
151            let video_listener = VideoListener::start_listening(video_tx.clone()).await?;
152            drone.inner.video_listener = Some(video_listener);
153        }
154
155        // expecting commands?
156        if let Some(command_rx) = options.command_receiver {
157            drone.inner.command_receiver = Some(Mutex::new(command_rx));
158        }
159
160        // tell drone to expect text SDK commands (not the private binary protocol)
161        println!("[Tello] putting drone in command mode...");
162        drone.send_expect_ok("command").await?;
163
164        // check battery
165        let b = drone.battery().await?;
166        if b < 10 {
167            println!("[Tello] WARNING low battery: {b}%");
168        }
169        else {
170            println!("[Tello] battery: {b}%");  
171        }
172
173        Ok(drone)
174    } 
175}
176
177impl Tello<Connected> {
178    /// Disconnect from the drone.
179    pub async fn disconnect(&self) -> Result<Tello<Disconnected>> {
180        println!("[Tello] DISCONNECT");
181
182        if let Some(state_listener) = &self.inner.state_listener {
183            state_listener.stop_listening().await?;
184        }
185
186        if let Some(video_listener) = &self.inner.video_listener {
187            video_listener.stop_listening().await?;
188        }
189
190        Ok(Tello { inner: Disconnected })
191    }
192
193    /// Sends a command to the drone using the simple Tello UDP protocol, returning the reponse.
194    ///
195    /// The basic flow from the user's point of view is
196    ///
197    ///    SEND command → drone does something → RECEIVE response when it's finished
198    ///
199    /// Messages are plain ASCII text, eg command `forward 10` → response `ok`
200    ///
201    /// - `command` the command to send, must be a valid Tello SDK command string
202    /// 
203    pub async fn send(&self, command: &str) -> Result<String> {
204        println!("[Tello] SEND {command}");
205
206        let s = &self.inner.sock;
207        s.send(command.as_bytes()).await?;
208
209        let response = self.recv().await?;
210
211        // the drone sends "forced stop" after "stop" after a delay which may
212        // arrive after more commands have been sent
213        if response == "forced stop" {
214            self.on_forced_stop();
215
216            // try again
217            self.recv().await
218        }
219        else {
220            Ok(response)
221        }          
222    }
223
224    async fn recv(&self) -> Result<String> {
225        let s = &self.inner.sock;
226        let mut buf = vec![0; 256];        
227        let n = s.recv(&mut buf).await?;
228
229        buf.truncate(n);
230        let r = String::from_utf8(buf)?;
231        let response = r.trim().to_string();
232
233        println!("[Tello] RECEIVED {response}");
234
235        Ok(response)
236    }
237
238    fn on_forced_stop(&self) {
239        println!("[Tello] FORCED STOP");
240    }
241
242    /// Sends a command, resolving to an error if the response is not "ok"
243    ///
244    /// - `command` the command to send, must be a valid Tello SDK command string
245    /// 
246    pub async fn send_expect_ok(&self, command: &str) -> Result<()> {
247        match self.send(command).await {
248            Ok(response) => {
249                if response == "ok" {
250                    Ok(())
251                }
252                else {
253                    Err(TelloError::from_not_ok_response(response))
254                }
255            }
256            Err(err) => Err(err)
257        }
258    }
259
260    /// Sends a command with a single value, resolving to an error if the 
261    /// response is not "ok"
262    ///
263    /// - `command` the command to send, must be a valid Tello SDK command string
264    /// - `value` the value to append to the command
265    /// 
266    pub async fn send_value_expect_ok<T: std::fmt::Display>(&self, command: &str, value: T) -> Result<()> {
267        match self.send(&format!("{command} {value}")).await {
268            Ok(response) => {
269                if response == "ok" {
270                    Ok(())
271                }
272                else {
273                    Err(TelloError::from_not_ok_response(response))
274                }
275            }
276            Err(err) => Err(err)
277        }
278    }
279
280    /// Sends a command, expecting no response at all from the drone.
281    ///
282    /// - `command` the command to send, must be a valid Tello SDK command string
283    /// 
284    pub async fn send_expect_nothing(&self, command: &str) -> Result<()> {
285        println!("[Tello] SEND {command}");
286
287        let s = &self.inner.sock;
288        s.send(command.as_bytes()).await?;
289
290        Ok(())
291    }
292
293    /// Sends a command, expecting a response that can be parsed as type `T` from the drone.
294    ///
295    /// - `command` the command to send, must be a valid Tello SDK command string
296    /// 
297    pub async fn send_expect<T: std::str::FromStr>(&self, command: &str) -> Result<T> {
298        let r = self.send(command).await?;
299        let v = r.parse::<T>().map_err(|_| TelloError::ParseError { msg: format!("unexpected response: \"{r}\"")})?;
300        Ok(v)
301    }
302
303    /// The unique drone serial number.
304    pub async fn serial_number(&self) -> Result<String> {
305        self.send("sn?").await
306    }
307
308    /// The Tello SDK version.
309    pub async fn sdk_version(&self) -> Result<String> {
310        self.send("sdk?").await
311    }
312
313    /// The drone battery level as a percentage.
314    pub async fn battery(&self) -> Result<u8> {
315        self.send_expect::<u8>("battery?").await
316    }
317
318    /// The WiFi signal to noise ratio as a percentage.
319    pub async fn wifi_signal_to_noise_ratio(&self) -> Result<u8> {
320        self.send_expect::<u8>("wifi?").await
321    }
322
323    /// The flight time in seconds, requested directly from the drone.
324    pub async fn flight_time(&self) -> Result<u16> {
325        self.send_expect::<u16>("time?").await
326    }
327
328    /// Immediately stop all motors.
329    ///
330    /// warning! this will make the drone drop like a brick!
331    ///
332    pub async fn emergency_stop(&self) -> Result<()> {
333        self.send_expect_nothing("emergency").await
334    }
335
336    /// Take off and hover.
337    pub async fn take_off(&self) -> Result<()> {
338        self.send_expect_ok("takeoff").await
339    }
340
341    /// Land and stop motors.
342    pub async fn land(&self) -> Result<()> {
343        self.send_expect_ok("land").await
344    }
345
346    /// The drone speed in cm/s, requested directly from the drone.
347    pub async fn speed(&self) -> Result<f32> {
348        self.send_expect::<f32>("speed?").await
349    }
350
351    /// Set the forward speed.
352    /// 
353    /// - `speed` Desired speed, 10-100 cm/s
354    ///
355    pub async fn set_speed(&self, speed: u8) -> Result<()> {
356        self.send_value_expect_ok("speed", speed).await
357    }
358
359    /// Wait for the given length of time.
360    ///
361    /// - `duration` The time to wait
362    ///
363    pub async fn wait(&self, duration:Duration) -> Result<()> {
364        println!("[Tello] waiting for {duration:#?}");
365        sleep(duration).await;
366        Ok(())
367    }    
368
369    /// Stop and hover in place.
370    pub async fn stop(&self) -> Result<()> {
371        // will also trigger a "forced stop" response
372        self.send_expect_ok("stop").await
373    }
374
375    /// Turn clockwise.
376    ///
377    /// - `degrees` Angle in degrees 1-360°
378    ///
379    pub async fn turn_clockwise(&self, degrees: u16) -> Result<()> {
380        self.send_value_expect_ok("cw", degrees).await   
381    }
382
383    /// Turn counter-clockwise.
384    ///
385    /// - `degrees` Angle in degrees 1-360°
386    pub async fn turn_counterclockwise(&self, degrees: u16) -> Result<()> {
387        self.send_value_expect_ok("ccw", degrees).await   
388    }
389
390    /// Move straight up.
391    ///
392    /// - `distance` Distance to travel, 20-500 cm
393    ///
394    pub async fn move_up(&self, distance: u16) -> Result<()> {
395        self.send_value_expect_ok("up", distance).await
396    }
397
398    /// Move straight down.
399    ///
400    /// - `distance` Distance to travel, 20-500 cm
401    ///
402    pub async fn move_down(&self, distance: u16) -> Result<()> {
403        self.send_value_expect_ok("down", distance).await
404    }
405    
406    /// Move straight left.
407    ///
408    /// - `distance` Distance to travel, 20-500 cm
409    ///
410    pub async fn move_left(&self, distance: u16) -> Result<()> {
411        self.send_value_expect_ok("left", distance).await
412    }
413    
414    /// Move straight right.
415    ///
416    /// - `distance` Distance to travel, 20-500 cm
417    ///
418    pub async fn move_right(&self, distance: u16) -> Result<()> {
419        self.send_value_expect_ok("right", distance).await
420    }
421    
422    /// Move straight forwards.
423    ///
424    /// - `distance` Distance to travel, 20-500 cm
425    ///
426    pub async fn move_forward(&self, distance: u16) -> Result<()> {
427        self.send_value_expect_ok("forward", distance).await
428    }
429    
430    /// Move straight backwards.
431    ///
432    /// - `distance` Distance to travel, 20-500 cm
433    ///
434    pub async fn move_back(&self, distance: u16) -> Result<()> {
435        self.send_value_expect_ok("back", distance).await
436    }
437
438    /// Flip left.
439    ///
440    /// *nb* fails if battery is low
441    /// 
442    pub async fn flip_left(&self) -> Result<()> {
443        self.send_expect_ok("flip l").await
444    }        
445
446    /// Flip right.
447    ///
448    /// *nb* fails if battery is low 
449    ///
450    pub async fn flip_right(&self) -> Result<()> {
451        self.send_expect_ok("flip r").await
452    }        
453
454    /// Flip forward.
455    ///
456    /// *nb* fails if battery is low 
457    ///
458    pub async fn flip_forward(&self) -> Result<()> {
459        self.send_expect_ok("flip f").await
460    }        
461
462    /// Flip back.
463    ///
464    /// *nb* fails if battery is low 
465    ///
466    pub async fn flip_back(&self) -> Result<()> {
467        self.send_expect_ok("flip b").await
468    }        
469
470    /// Start video as stream of h264-encoded frames.
471    ///
472    /// Use `TelloOption::with_video()` to set up a channel for receiving the
473    /// video frames.
474    ///
475    /// *nb* You must consume the frame data! The channel is unlimited and 
476    /// will eventually use up all available memory if you don't.
477    ///
478    pub async fn start_video(&self) -> Result<()> {
479        self.send_expect_ok("streamon").await
480    }        
481
482    /// Stop video streaming.
483    pub async fn stop_video(&self) -> Result<()> {
484        self.send_expect_ok("streamoff").await
485    }
486
487    /// Remote control'
488    ///
489    /// All arguments are -100 to 100 (not sure what units)
490    /// - `left_right` Movement sideways
491    /// - `forwards_backwards` Forwards/backwards
492    /// - `up_down` Vertical movement
493    /// - `yaw` Turn left or right
494    ///
495    pub async fn remote_control(&self, left_right:i8, forwards_backwards:i8, up_down:i8, yaw:i8) -> Result<()> {
496        self.send_expect_nothing(&format!("rc {left_right} {forwards_backwards} {up_down} {yaw}")).await
497    }
498
499
500    //////////////////////////////////////////////////////////////////////////
501
502    pub async fn handle_commands(&self) -> Result<()> {
503        if let Some(command_receiver) = &self.inner.command_receiver { 
504            let mut command_rx = command_receiver.lock().await;
505            while let Some(command) = command_rx.recv().await {
506                match command {
507                    TelloCommand::TakeOff => self.take_off().await?,
508                    TelloCommand::Land => self.land().await?,
509                    TelloCommand::StopAndHover => self.stop().await?,
510                    TelloCommand::EmergencyStop => self.emergency_stop().await?,
511                    TelloCommand::RemoteControl { left_right, forwards_backwards, up_down, yaw } => 
512                        self.remote_control(left_right, forwards_backwards, up_down, yaw).await?,
513                    TelloCommand::FlipLeft => self.flip_left().await?,
514                    TelloCommand::FlipRight => self.flip_right().await?,
515                    TelloCommand::FlipForward => self.flip_forward().await?,
516                    TelloCommand::FlipBack => self.flip_back().await?
517                 }
518            }
519        }
520    
521        Ok(())
522
523    }
524
525}