Code examples

More code examples can be found in the repository examples directory.

Take off and land

Connect to the drone, take off and land immediately. You must join the drone’s own wifi network first!

#!/usr/bin/env python3

import asyncio
from tello_asyncio import Tello


async def main():
    drone = Tello()
    try:
        await drone.wifi_wait_for_network(prompt=True)
        await drone.connect()
        await drone.takeoff()
        await drone.land()
    finally:
        await drone.disconnect()


# Python 3.7+
# asyncio.run(main())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Flip

Do a flip in each direction.

#!/usr/bin/env python3

import asyncio
from tello_asyncio import Tello, Direction


async def main():
    drone = Tello()
    try:
        await drone.wifi_wait_for_network(prompt=True)
        await drone.connect()
        await drone.takeoff()
        for direction in [
            Direction.LEFT,
            Direction.RIGHT,
            Direction.FORWARD,
            Direction.BACK,
        ]:
            await drone.flip(direction)
        await drone.land()
    finally:
        await drone.disconnect()


# Python 3.7+
# asyncio.run(main())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

State

Get details of the drone’s movement, battery level and other state.

#!/usr/bin/env python3

import asyncio
from tello_asyncio import Tello


def on_drone_state(drone, state):
    print(f"height: {state.height}cm, battery: {state.battery}%")
    v = state.velocity
    print(f"velocity x: {v.x}cm/s, y: {v.y}cm/s, z: {v.z}cm/s")


async def main():
    drone = Tello(on_state=on_drone_state)
    try:
        await drone.wifi_wait_for_network(prompt=True)
        await drone.connect()
        await drone.takeoff()
        await drone.land()
    finally:
        await drone.disconnect()

    print(f"total flight time: {drone.motor_time}s")
    print(f"temperature: {drone.temperature.low}-{drone.temperature.high}°C")


# Python 3.7+
# asyncio.run(main())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Go to mission pads

Fly from one mission pad to another. The mission pads must be arranged so that the drone should be able to see them.

#!/usr/bin/env python3

# Fly along a route marked by three mission pads arranged more or less linearly, about 1.5 meters apart.
# The state messages show that the Tello senses the pads as it goes.

import asyncio
from tello_asyncio import Tello, Vector


async def main():
    drone = Tello()

    def output_state():
        print(
            f"mission pad: {drone.mission_pad}, position: {drone.mission_pad_position}"
        )

    try:
        await drone.wifi_wait_for_network(prompt=True)
        await drone.connect()
        await drone.enable_mission_pads()
        await drone.takeoff()
        output_state()
        await drone.go_to(relative_position=Vector(0, 0, 120), speed=50, mission_pad=1)
        output_state()
        await drone.go_to(
            relative_position=Vector(150, 0, 120), speed=50, mission_pad=1
        )
        output_state()
        await drone.go_to(relative_position=Vector(0, 0, 120), speed=50, mission_pad=2)
        output_state()
        await drone.go_to(
            relative_position=Vector(150, 0, 120), speed=50, mission_pad=2
        )
        output_state()
        await drone.go_to(relative_position=Vector(0, 0, 120), speed=50, mission_pad=3)
        output_state()
        await drone.land()
    finally:
        await drone.disconnect()


# Python 3.7+
# asyncio.run(main())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Video save frames

Connect to streaming video, decode and save frames to file as JPEG images.

Please note that this example has a couple of extra dependencies to install before running:

  • h264decoder for decoding h.264 encoded frames from the drone

  • Pillow image library for saving the JPEGs

#!/usr/bin/env python3
from pathlib import Path
import asyncio
import h264decoder  # see https://github.com/DaWelter/h264decoder for installation instructions
from PIL import Image  # requires Pillow

from tello_asyncio import Tello

Path("video_save_frames").mkdir(exist_ok=True)

i = 1
decoder = h264decoder.H264Decoder()


def on_video_frame(drone, frame):
    global i

    try:
        (frame_info, num_bytes) = decoder.decode_frame(frame)
        (frame_data, width, height, row_size) = frame_info
        if width and height:
            image = Image.frombytes("RGB", (width, height), frame_data)
            file_path = f"video_save_frames/frame-{i}.jpg"
            image.save(file_path)
            i += 1
    except Exception as e:
        print(e)


async def main():
    drone = Tello()
    try:
        await drone.wifi_wait_for_network(prompt=True)
        await drone.connect()
        await drone.start_video(on_video_frame)
        await drone.takeoff()
        await drone.turn_clockwise(360)
        await drone.land()
    finally:
        await drone.stop_video()
        await drone.disconnect()


# Python 3.7+
# asyncio.run(main())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Video in OpenCV

Display streaming video in an OpenCV window.

The key point is that the OpenCV GUI expects to have full ownership of the main thread with its own event loop, so the drone control asyncio event loop runs separately in a worker thread.

#!/usr/bin/env python3

##############################################################################
#
# A minimal proof of concept of controlling the drone while capturing and
# showing video using OpenCV.
#
# The OpenCV UI must run in the main thread, so the drone control runs in a
# worker thread with its own asyncio event loop.
#
# Please note:
#   - If OpenCV fails to capture any video it gives up without showing the
#     window
#   - The video plays a few seconds behind the live action. This appears to
#     be a limitation of the OpenCV capture.read() / cv2.imshow() approach
#
##############################################################################

import asyncio
from threading import Thread

import cv2  # requires python-opencv

from tello_asyncio import Tello, VIDEO_URL

print("[main thread] START")

##############################################################################
# drone control in worker thread


def fly():
    print("[fly thread] START")

    async def main():
        drone = Tello()
        try:
            await asyncio.sleep(1)
            await drone.wifi_wait_for_network(prompt=True)
            await drone.connect()
            await drone.start_video(connect=False)
            await drone.takeoff()
            await drone.turn_clockwise(360)
            await drone.land()
        finally:
            await drone.stop_video()
            await drone.disconnect()

    # Python 3.7+
    # asyncio.run(main())
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(main())

    print("[fly thread] END")


# needed for drone.wifi_wait_for_network() in worker thread in Python < 3.8
asyncio.get_child_watcher()

fly_thread = Thread(target=fly, daemon=True)
fly_thread.start()

##############################################################################
# Video capture and GUI in main thread

print(f"[main thread] OpenCV capturing video from {VIDEO_URL}")
print(
    f"[main thread] Press Ctrl-C or any key with the OpenCV window focussed to exit (the OpenCV window may take some time to close)"
)


capture = None
try:
    capture = cv2.VideoCapture(VIDEO_URL)
    capture.open(VIDEO_URL)

    while True:
        # grab and show video frame in OpenCV window
        grabbed, frame = capture.read()
        if grabbed:
            cv2.imshow("tello-asyncio", frame)

        # process OpenCV events and exit if any key is pressed
        if cv2.waitKey(1) != -1:
            break
except KeyboardInterrupt:
    pass
finally:
    # tidy up
    if capture:
        capture.release()
    cv2.destroyAllWindows()

print("[main thread] END")