Safety system API.
This module provides the API for arming/disarming robots and managing safety state.
The disarm/1 callback that components implement is now defined in BB.Controller
and BB.Actuator behaviours.
Safety States
:disarmed- Robot is safely disarmed, all disarm callbacks succeeded:armed- Robot is armed and ready to operate:disarming- Disarm in progress, callbacks running concurrently:error- Disarm attempted but one or more callbacks failed; hardware may not be safe
When in :error state, the robot cannot be armed until force_disarm/1 is called
to acknowledge the error and reset to :disarmed.
Disarm callbacks run concurrently with a timeout. If any callback fails or times out,
the robot transitions to :error state.
Implementing Disarm Callbacks
Controllers and actuators implement the disarm/1 callback via their behaviours:
defmodule MyActuator do
use GenServer
use BB.Actuator
@impl BB.Actuator
def disarm(opts) do
pin = Keyword.fetch!(opts, :pin)
MyHardware.disable(pin)
:ok
end
def init(opts) do
BB.Safety.register(__MODULE__,
robot: opts[:bb].robot,
path: opts[:bb].path,
opts: [pin: opts[:pin]]
)
# ...
end
endIf your actuator doesn't need special disarm logic, you can implement a no-op:
@impl BB.Actuator
def disarm(_opts), do: :okImportant Limitations
The BEAM virtual machine provides soft real-time guarantees, not hard real-time. Disarm callbacks may be delayed by garbage collection, scheduler load, or other system activity. For safety-critical applications, always implement hardware-level safety controls as your primary protection.
See the Safety documentation topic for detailed recommendations.
Summary
Functions
Arm the robot.
Check if a robot is armed.
Disarm the robot.
Check if a robot is currently disarming.
Force disarm from error state.
Check if a robot is in error state.
Register a safety handler (actuator/sensor/controller).
Report a hardware error from a component.
Get current safety state for a robot.
Functions
Arm the robot.
Goes through the safety controller GenServer to ensure proper state transitions.
Cannot arm if robot is in :error state - must call force_disarm/1 first.
Returns :ok or {:error, :already_armed | :in_error | :not_registered}.
Check if a robot is armed.
Fast ETS read - does not go through GenServer.
Disarm the robot.
Goes through the safety controller GenServer. Calls all registered disarm/1
callbacks before updating state. If any callback fails, the robot transitions
to :error state instead of :disarmed.
Options
:timeout- timeout in milliseconds for each disarm callback. Defaults to 5000ms.
Returns :ok or {:error, :already_disarmed | {:disarm_failed, failures}}.
Check if a robot is currently disarming.
Returns true while disarm callbacks are running.
Fast ETS read - does not go through GenServer.
Force disarm from error state.
Use this function to acknowledge a failed disarm operation and reset the
robot to :disarmed state. This should only be called after manually
verifying that hardware is in a safe state.
WARNING: This bypasses safety checks. Only use when you have manually verified that all actuators are disabled and the robot is safe.
Returns :ok or {:error, :not_in_error | :not_registered}.
Check if a robot is in error state.
Returns true if a disarm operation failed and the robot requires
manual intervention via force_disarm/1.
Fast ETS read - does not go through GenServer.
Register a safety handler (actuator/sensor/controller).
Called by processes in their init/1. The opts should contain all
hardware-specific parameters needed to call disarm/1 without GenServer state.
Writes directly to ETS to avoid blocking on the Controller's mailbox.
Options
:robot(required) - The robot module:path(required) - The path to this component (for logging):opts- Hardware-specific options passed todisarm/1
Example
BB.Safety.register(__MODULE__,
robot: MyRobot,
path: [:arm, :shoulder_joint, :servo],
opts: [pin: 18]
)
Report a hardware error from a component.
Publishes a BB.Safety.HardwareError message to [:safety, :error] for
subscribers to handle. This is a pure notification - it does not disarm the
robot or change safety state.
Components that detect an unrecoverable hardware fault should raise or
exit instead of (or in addition to) calling this function. The supervisor
will restart the offending process; if the restart budget on the topology
supervisor is exhausted, the safety controller will force-disarm the robot.
This is the OTP-native way to signal hardware failure: let it crash, and
let supervision escalate.
Parameters
robot_module- The robot modulepath- Path to the component reporting the error (e.g.,[:dynamixel, :servo_1])error- Component-specific error details
Example
# In a controller detecting servo overheating - publish for observers,
# then crash so the supervisor decides whether to escalate:
BB.Safety.report_error(MyRobot, [:dynamixel, :servo_1], {:hardware_error, 0x04})
raise BB.Error.Hardware.Overheat, servo: 1
Get current safety state for a robot.
Fast ETS read - does not go through GenServer.
Returns :armed, :disarmed, :disarming, or :error.