# `BB.Servo.PCA9685.Actuator`
[🔗](https://github.com/beam-bots/bb_servo_pca9685/blob/main/lib/bb/servo/pca9685/actuator.ex#L5)

An actuator that uses a PCA9685 controller to drive a servo.

Configuration is derived from the joint's `motor_profile` injected by
`BB.Actuator.Server`:

- Position limits from `motor_profile.motor_lower` / `motor_upper`
- Velocity limit from `motor_profile.motor_velocity_limit`
- PWM range maps linearly to the motor's position range
  (`motor_lower → min_pulse`, `motor_upper → max_pulse`)

When a position command is received, the actuator:
1. Clamps the position to motor limits
2. Converts to PWM pulse width
3. Sends PWM command to the PCA9685 controller
4. Publishes a `BB.Message.Actuator.BeginMotion` via
   `BB.Actuator.publish_begin_motion/3` (which handles the
   motor → joint-space conversion)

## Example DSL Usage

    controller :pca9685, {BB.Servo.PCA9685.Controller, bus: "i2c-1", address: 0x40}

    joint :shoulder, type: :revolute do
      limit lower: ~u(-45 degree), upper: ~u(45 degree), velocity: ~u(60 degree_per_second)

      actuator :servo, {BB.Servo.PCA9685.Actuator, channel: 0, controller: :pca9685}
      sensor :feedback, {BB.Sensor.OpenLoopPositionEstimator, actuator: :servo}
    end

# `disarm`

Disable the servo by setting pulse width to 0.

Called by `BB.Safety.Controller` when the robot is disarmed or crashes.
It needs no actuator state - the robot module, controller name, and channel
come from the opts provided during registration - but the write is still routed
through the controller, so it requires the controller process to be alive. If the
controller is unreachable the call exits and the disarm fails, driving the robot
into the `:error` state.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
