extern crate tello_edu;
use sdl2::controller::Axis;
use sdl2::controller::Button;
use sdl2::event::Event;
use tello_edu::{TelloOptions, Tello, Result, TelloCommandSender, TelloCommand};
fn main() {
let mut options = TelloOptions::default();
let command_sender = options.with_command();
std::thread::spawn(move || {
let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
tokio_runtime.block_on(async {
fly(options).await.unwrap();
});
});
run_control(command_sender).expect("failed to run control");
}
fn run_control(command_sender: TelloCommandSender) -> anyhow::Result<(), String> {
sdl2::hint::set("SDL_JOYSTICK_THREAD", "1");
let sdl_context = sdl2::init()?;
let game_controller_subsystem = sdl_context.game_controller()?;
let num_controllers = game_controller_subsystem
.num_joysticks()
.map_err(|e| format!("can't enumerate controllers: {}", e))?;
if num_controllers == 0 {
return Err("no game controllers found".to_string());
}
let controller = (0..num_controllers)
.find_map(|id| {
if !game_controller_subsystem.is_game_controller(id) {
println!("{} is not a game controller", id);
return None;
}
match game_controller_subsystem.open(id) {
Ok(c) => {
println!("using controller \"{}\"", c.name());
Some(c)
}
Err(e) => {
println!("failed to open controller {id}: {e:?}");
None
}
}
})
.expect("failed to use any controller");
let mut left_right:i8 = 0;
let mut forwards_backwards:i8 = 0;
let mut up_down:i8 = 0;
let mut yaw:i8 = 0;
for event in sdl_context.event_pump()?.wait_iter() {
match event {
Event::ControllerButtonDown { button: Button::LeftShoulder, .. }
| Event::ControllerButtonDown { button: Button::RightShoulder, .. } => {
if controller.button(Button::LeftShoulder) && controller.button(Button::RightShoulder) {
command_sender.send(TelloCommand::EmergencyStop)
}
else {
Ok(())
}
}
Event::ControllerButtonUp { button: Button::Start, .. } => {
command_sender.send(TelloCommand::TakeOff)
}
Event::ControllerButtonDown { button: Button::X, .. } => {
left_right = 0;
forwards_backwards = 0;
up_down = 0;
yaw = 0;
command_sender.send(TelloCommand::Land)
}
Event::ControllerButtonDown { button: Button::B, .. } => {
left_right = 0;
forwards_backwards = 0;
up_down = 0;
yaw = 0;
command_sender.send(TelloCommand::RemoteControl { left_right, forwards_backwards, up_down, yaw} )
}
Event::ControllerAxisMotion { axis: Axis::LeftY, value, .. } => {
forwards_backwards = -remote_control_value(value);
command_sender.send(TelloCommand::RemoteControl { left_right, forwards_backwards, up_down, yaw} )
}
Event::ControllerAxisMotion { axis: Axis::LeftX, value, .. } => {
yaw = remote_control_value(value);
command_sender.send(TelloCommand::RemoteControl { left_right, forwards_backwards, up_down, yaw} )
}
Event::ControllerAxisMotion { axis: Axis::RightY, value, .. } => {
up_down = -remote_control_value(value);
command_sender.send(TelloCommand::RemoteControl { left_right, forwards_backwards, up_down, yaw} )
}
Event::ControllerAxisMotion { axis: Axis::RightX, value, .. } => {
left_right = remote_control_value(value);
command_sender.send(TelloCommand::RemoteControl { left_right, forwards_backwards, up_down, yaw} )
}
Event::ControllerButtonDown { button: Button::DPadLeft, .. } => {
command_sender.send(TelloCommand::FlipLeft)
}
Event::ControllerButtonDown { button: Button::DPadRight, .. } => {
command_sender.send(TelloCommand::FlipRight)
}
Event::ControllerButtonDown { button: Button::DPadUp, .. } => {
command_sender.send(TelloCommand::FlipForward)
}
Event::ControllerButtonDown { button: Button::DPadDown, .. } => {
command_sender.send(TelloCommand::FlipBack)
}
Event::Quit { .. } => break,
_ => Ok(()),
}.map_err(|err| format!("error sending command: {err}"))?;
}
Ok(())
}
const DEAD_ZONE:i16 = 10;
const AXIS_MAX:f32 = 32767.0;
fn normalize_axis_value(value:i16) -> Option<f32> {
if value > DEAD_ZONE || value < -DEAD_ZONE {
let normalized_value = value as f32 / AXIS_MAX;
if normalized_value > 1.0 {
Some(1.0)
}
else if normalized_value < -1.0 {
Some(-1.0)
}
else {
Some(normalized_value)
}
}
else {
None
}
}
fn remote_control_value(value:i16) -> i8 {
match normalize_axis_value(value) {
Some(v) => (v * 100.0) as i8,
None => 0
}
}
async fn fly(options:TelloOptions) -> Result<()> {
let drone = Tello::new()
.wait_for_wifi().await?;
let drone = drone.connect_with(options).await?;
drone.handle_commands().await?;
Ok(())
}