Safety
View SourceSafety is a first-class concern in ex_drone. Every command passes through a safety pipeline before reaching the drone adapter.
Why Safety Matters
Drones are physical devices that can cause injury. A software bug should never result in a dangerous flight. The safety pipeline enforces limits by default.
Safety Pipeline
Command Requested
-> Emergency? -> Bypass all, send immediately
-> Validate Command Args -> Reject if out of Tello SDK range
-> Check Mode -> Reject if not in a valid state
-> Allowlist? -> Reject if not allowed
-> Flying Requirement? -> Reject if state does not match
-> Altitude? -> Reject if exceeds max
-> Distance? -> Reject if exceeds max
-> Battery? -> Reject takeoff if too low
-> Geofence? -> Reject if outside area
-> Emit Telemetry
-> Send to Adapter
-> Parse Result
-> Update Vehicle StateCommand Argument Limits
Movement arguments are validated against the Tello SDK protocol ranges
before any policy checks. Out-of-range values are rejected with
{:error, :safety, reason}:
move/3distance: 20-500 cm (:invalid_distance)rotate/3degrees: 1-3600 (:invalid_degrees)set_speed/2speed: 10-100 cm/s (:invalid_speed)hover/2seconds: must be a positive integer (:invalid_seconds)
Safety Policy Configuration
Drone.Safety.Policy.new(
max_altitude_cm: 300,
max_distance_cm: 1000,
min_battery_percent: 15,
battery_warning_percent: 20,
indoor: false,
prop_guards: false,
dry_run: false,
allowlist: nil,
geofence: Drone.Safety.Geofence.radius(500)
)Presets
Drone.Safety.Policy.default()-- Outdoor defaults (3m altitude, 10m distance)Drone.Safety.Policy.indoor()-- Indoor defaults (2m altitude, 5m distance)Drone.Safety.Policy.unrestricted()-- No limits (use with caution)
You can also pass a %Policy{} struct directly to Drone.connect/2:
policy = Drone.Safety.Policy.new(max_altitude_cm: 250, indoor: true)
{:ok, drone} = Drone.connect(:sim, name: :custom, safety: policy)Emergency Commands
Emergency commands always bypass the safety pipeline:
Drone.emergency(drone) # Always succeeds, stops motors immediatelyDry-Run Mode
Validate missions without flying:
{:ok, drone} = Drone.connect(:sim, name: :test, safety: [dry_run: true])
Drone.connect_sdk(drone)
Drone.takeoff(drone) # Returns :ok, validated but no command sent to the droneGeofencing
Restrict flight to a circular area:
geofence = Drone.Safety.Geofence.circle({0, 0}, 500)
{:ok, drone} = Drone.connect(:sim, name: :geofenced,
safety: [geofence: geofence]
)Or a polygon:
geofence = Drone.Safety.Geofence.polygon([{0, 0}, {1000, 0}, {1000, 1000}, {0, 1000}])Command Allowlists
Restrict which commands are allowed:
{:ok, drone} = Drone.connect(:sim, name: :observer,
safety: [allowlist: [:sdk_mode, :query]]
)The :emergency command is always allowed regardless of the allowlist.