Drone. Adapter behaviour
(ex_drone v0.3.0)
View Source
Behaviour definition for drone adapters.
Every drone adapter must implement this behaviour. The adapter is responsible
for all communication with the physical (or simulated) drone. The
Drone.Vehicle GenServer calls adapter callbacks, passing opaque adapter
state.
Built-in adapters: Drone.Adapters.Sim, Drone.Adapters.Tello,
Drone.Adapters.Crazyflie.
Implementing an Adapter
defmodule Drone.Adapters.FakeDrone do
@behaviour Drone.Adapter
defstruct [:connected, battery: 100, z: 0, flying: false, mode: :idle]
@impl Drone.Adapter
def connect(opts) do
battery = Keyword.get(opts, :battery, 100)
{:ok, %__MODULE__{connected: true, battery: battery}}
end
@impl Drone.Adapter
def command(%__MODULE__{} = state, %Drone.Command{type: :sdk_mode}) do
{:ok, :ok, %{state | mode: :sdk_mode}}
end
def command(%__MODULE__{mode: :sdk_mode} = state, %Drone.Command{type: :takeoff}) do
{:ok, :ok, %{state | flying: true, z: 30, mode: :flying}}
end
def command(%__MODULE__{} = state, %Drone.Command{type: :land}) do
{:ok, :ok, %{state | flying: false, z: 0, mode: :sdk_mode}}
end
def command(%__MODULE__{} = state, %Drone.Command{type: :emergency}) do
{:ok, :ok, %{state | flying: false, mode: :emergency}}
end
def command(state, %Drone.Command{type: :query, args: args}) do
value =
case Keyword.fetch!(args, :type) do
:battery -> state.battery
:height -> state.z
other -> {:unsupported_query, other}
end
{:ok, value, state}
end
def command(state, _cmd), do: {:error, :unsupported_command, state}
@impl Drone.Adapter
def telemetry(%__MODULE__{} = state) do
{:ok,
%{
x: 0,
y: 0,
z: state.z,
yaw: 0,
battery: state.battery,
flying: state.flying,
mode: state.mode
}, state}
end
@impl Drone.Adapter
def disconnect(%__MODULE__{}), do: :ok
end
{:ok, drone} = Drone.connect(Drone.Adapters.FakeDrone, name: :fake)See docs/adapter_authoring.md for a complete guide.
Summary
Types
Opaque adapter state held by Drone.Vehicle.
Callbacks
Returns capability metadata for the connected adapter.
Executes a single command against the drone.
Opens a connection and returns initial adapter state.
Closes the adapter connection and releases resources.
Returns a telemetry snapshot from the adapter.
Functions
Returns capabilities for an adapter state, with a Tello-like default.
Returns the adapter module for a given adapter identifier.
Types
@type state() :: term()
Opaque adapter state held by Drone.Vehicle.
Each adapter defines its own representation. It may be a map, struct, or any term. The Vehicle never inspects it except to pass it back into callbacks.
Examples
%{socket: port(), drone_ip: {192, 168, 10, 1}}
%Drone.Adapters.Sim.State{x: 0, y: 0, z: 30, battery: 98}
Callbacks
@callback capabilities(state :: state()) :: Drone.Adapter.Capabilities.t()
Returns capability metadata for the connected adapter.
Optional. When omitted, callers treat the adapter as Tello-shaped
(Drone.Adapter.Capabilities.tello_like/0).
Parameters
state(state/0) — current adapter state
Returns
Drone.Adapter.Capabilities.t().
Example implementation
@impl Drone.Adapter
def capabilities(%__MODULE__{positioning: positioning}) do
Drone.Adapter.Capabilities.crazyflie(positioning: positioning)
end
@callback command(state :: state(), command :: Drone.Command.t()) :: {:ok, reply :: term(), new_state :: state()} | {:error, reason :: term(), new_state :: state()}
Executes a single command against the drone.
The Vehicle calls this only after safety approval (except emergency, which bypasses safety but still uses this callback).
Parameters
state(state/0) — current adapter statecommand(Drone.Command.t()) — normalized command struct
Returns
{:ok, reply, new_state}—replyis typically:okor a query value{:error, reason, new_state}— command failed; state may still update
Example implementation
@impl Drone.Adapter
def command(state, %Drone.Command{type: :takeoff}) do
:ok = send_udp(state, "takeoff")
{:ok, :ok, %{state | flying: true}}
end
def command(state, %Drone.Command{type: :query, args: [type: :battery]}) do
{:ok, percent} = send_udp_query(state, "battery?")
{:ok, percent, state}
end
Opens a connection and returns initial adapter state.
Called once when Drone.connect/2 starts the vehicle.
Parameters
opts(keyword()) — adapter-specific options fromDrone.connect/2after:name,:adapter, and:safetyare stripped. Examples:- Sim:
:battery,:initial_x,:initial_y,:failure_rate, ... - Tello:
:drone_ip,:command_port,:timeout, ...
- Sim:
Returns
{:ok, state()}— connected; state is stored on the Vehicle{:error, term()}— connection failed (vehicle exits)
Example implementation
@impl Drone.Adapter
def connect(opts) do
ip = Keyword.get(opts, :drone_ip, {192, 168, 10, 1})
{:ok, socket} = :gen_udp.open(0, [:binary, active: false])
{:ok, %{socket: socket, ip: ip}}
end
@callback disconnect(state :: state()) :: :ok
Closes the adapter connection and releases resources.
Optional callback — if omitted, disconnect is a no-op at the behaviour level (Vehicle still stops).
Parameters
state(state/0) — final adapter state
Returns
Always :ok.
Example implementation
@impl Drone.Adapter
def disconnect(%{socket: socket}) do
:gen_udp.close(socket)
:ok
end
Returns a telemetry snapshot from the adapter.
Used to seed and refresh vehicle state (position, battery, mode).
Parameters
state(state/0) — current adapter state
Returns
{:ok, map(), state()}— map should include keys like:x,:y,:z,:yaw,:battery,:flying,:modewhen available{:error, term(), state()}— snapshot failed
Example implementation
@impl Drone.Adapter
def telemetry(state) do
{:ok,
%{
x: state.x,
y: state.y,
z: state.z,
yaw: state.yaw,
battery: trunc(state.battery),
flying: state.flying,
mode: state.mode
}, state}
end
Functions
@spec capabilities(module(), state()) :: Drone.Adapter.Capabilities.t()
Returns capabilities for an adapter state, with a Tello-like default.
Parameters
module(module()) — adapter modulestate(state/0) — adapter state fromconnect/1
Returns
Drone.Adapter.Capabilities.t().
Returns the adapter module for a given adapter identifier.
Parameters
adapter(atom() | module()) —:sim,:tello,:crazyflie, or a module that implements this behaviour
Returns
{:ok, module()}— resolved module that exportsconnect/1{:error, :unknown_adapter}— unknown built-in key or unloaded module
Examples
{:ok, Drone.Adapters.Sim} = Drone.Adapter.resolve(:sim)
{:ok, Drone.Adapters.Tello} = Drone.Adapter.resolve(:tello)
{:ok, Drone.Adapters.Crazyflie} = Drone.Adapter.resolve(:crazyflie)
{:ok, MyApp.DroneAdapter} = Drone.Adapter.resolve(MyApp.DroneAdapter)