Adapter Authoring
View SourceThis guide explains how to create a custom drone adapter for ex_drone.
The Adapter Behaviour
All adapters must implement Drone.Adapter:
defmodule Drone.Adapter do
@callback connect(opts :: keyword()) :: {:ok, state()} | {:error, term()}
@callback command(state(), command()) :: {:ok, reply, new_state} | {:error, reason, new_state}
@callback telemetry(state()) :: {:ok, map(), state()} | {:error, term(), state()}
@callback disconnect(state()) :: :ok
endCreating an Adapter
1. Define the Module
defmodule Drone.Adapters.MyDrone do
@behaviour Drone.Adapter
defstruct [:connection, :position]
@impl Drone.Adapter
def connect(opts) do
# Open connection, return initial state
{:ok, %__MODULE__{position: {0, 0, 0}}}
end
@impl Drone.Adapter
def command(state, %Drone.Command{type: :takeoff}) do
# Send takeoff command, update state
{:ok, :ok, %{state | position: {0, 0, 30}}}
end
# ... implement other command types
@impl Drone.Adapter
def telemetry(state) do
{:ok, %{x: state.position.x, y: state.position.y}, state}
end
@impl Drone.Adapter
def disconnect(state) do
# Close connection
:ok
end
end2. Register the Adapter
Users can pass the module directly:
{:ok, drone} = Drone.connect(Drone.Adapters.MyDrone, name: :my_drone)Or add it to the built-in map in Drone.Adapter.resolve/1.
3. Handle All Command Types
Your adapter must handle (or gracefully reject) all command types:
:sdk_mode,:takeoff,:land,:emergency:move(with direction and distance):rotate(with direction and degrees):flip(with direction):hover(with seconds):speed(with speed value):stop:query(with type: battery, height, speed, time, wifi, sdk_version, serial_number)
4. State Management Rules
- The
stateterm is opaque. Use a struct for clarity. - Always return
new_statefromcommand/2, even on error. - The
telemetry/1callback should return at minimum:x,y,z,yaw,battery,flying,mode.
5. Error Conventions
Return errors as {:error, reason, new_state}:
:timeout-- No response from drone:connection_error-- Connection failed:not_in_sdk_mode-- Command sent before SDK mode:not_flying-- Movement when grounded:already_flying-- Takeoff when airborne
Testing
Test your adapter using the simulator pattern:
# Connect
{:ok, state} = MyAdapter.connect([])
# Send commands
{:ok, _, state} = MyAdapter.command(state, Drone.Command.takeoff())
# Check telemetry
{:ok, telemetry, _} = MyAdapter.telemetry(state)
assert telemetry.flying == true
# Disconnect
:ok = MyAdapter.disconnect(state)