# Add a servo

Add a servo to your machine’s configuration so you can control its angular position from the Viam app and from code.

## Concepts

A servo moves to a specific angle (typically 0-180 degrees) and holds that position. Continuous-rotation servos also exist; they use the same `Move` API, but the angle value maps to speed and direction rather than absolute position. Check the specific servo model’s reference page for how it interprets the value.

The built-in `gpio` servo model uses a single PWM-capable pin on a [board component](https://docs.viam.com/hardware/common-components/add-a-board/). Other servo models in the registry support serial, I2C, or dedicated servo-driver boards.

Search for `servo` in the [Viam registry](https://app.viam.com/registry) to see available models.

### Built-in models

- [`fake`](https://docs.viam.com/reference/components/servo/fake/) — A model used for testing, with no physical hardware.
- [`gpio`](https://docs.viam.com/reference/components/servo/gpio/) — Supports a hobby servo wired to a board that supports PWM, for example Raspberry Pi 5, Orange Pi, Jetson, or PCAXXXX.

Micro-RDK:

- [`gpio`](https://docs.viam.com/reference/components/servo/micro-rdk/gpio/) — _(no description)_.

### Registry modules

Viam-maintained servo modules:

| Module | Servos supported |
| --- | --- |
| [`viam:raspberry-pi`](https://app.viam.com/module/viam/raspberry-pi) | `rpi-servo` model for hobby servos on Raspberry Pi variants |

For servos not covered above, search for `servo` in the [Viam registry](https://app.viam.com/registry).

## Steps

### 1. Prerequisites

- Your machine is online in the Viam app.
- A [board component](https://docs.viam.com/hardware/common-components/add-a-board/) is configured.
- Your servo’s signal wire is connected to a PWM-capable GPIO pin, with power and ground wired appropriately.

### 2. Add a servo component

1. Click the **+** button.
2. Select **Blocks**.
3. For a standard hobby servo controlled by a PWM pin on your board, search for **gpio servo**.
4. Name your servo (for example, `pan-servo`) and click **Add to machine**.

### 3. Configure servo attributes

```json
{
  "board": "my-board",
  "pin": "12"
}
```

| Attribute               | Type   | Required | Description                                                 |
| ----------------------- | ------ | -------- | ----------------------------------------------------------- |
| `board`                | string | Yes      | Name of the board component.                                |
| `pin`                  | string | Yes      | GPIO pin for the servo signal wire.                        |
| `min_angle_deg`       | float  | No       | Minimum angle. Default: `0`.                                |
| `max_angle_deg`       | float  | No       | Maximum angle. Default: `180`.                              |
| `starting_position_deg`| float  | No       | Position on startup. Default: `0`.                          |
| `frequency_hz`        | int    | No       | PWM frequency. Default: `300`. Most servos expect 50-330 Hz.|

If your servo doesn’t reach its full range or jitters at the extremes, adjust the pulse width:

| Attribute       | Type | Description                                     |
| --------------- | ---- | ----------------------------------------------- |
| `min_width_us` | int  | Minimum pulse width in microseconds (>450).    |
| `max_width_us` | int  | Maximum pulse width in microseconds (<2500).    |

### 4. Save and test

Click **Save**, then expand the **Test** section.

- Enter a value in the **Desired angle (º)** field (the built-in `gpio` model accepts 0-180), then click **Execute** to move the servo.
- Use the **Zero** or **Current position** buttons to quickly fill the input.
- The servo should move smoothly and hold its position.

## Try it

Sweep the servo through a range of positions.

To get the credentials for the code below, go to your machine’s page in the Viam app, click the **CONNECT** tab, and select **API keys**. Copy the **Key** and **ID**. Then click the **CONFIGURE** tab, and click **Details**, and copy the **Remote address**. If you’re using real hardware, you’ll see the servo sweep through positions when you run the code below.

- [Python](https://docs.viam.com/hardware/common-components/add-a-servo/#tabset-hardwarecommon-componentsadd-a-servo-1-0)
- [Go](https://docs.viam.com/hardware/common-components/add-a-servo/#tabset-hardwarecommon-componentsadd-a-servo-1-1)

Install the Viam Python SDK in a virtual environment by following [Install the Python SDK](https://docs.viam.com/reference/sdks/python/python-venv/).

Save this as `servo_test.py`:

```python
import asyncio
from viam.robot.client import RobotClient
from viam.components.servo import Servo

async def main():
    opts = RobotClient.Options.with_api_key(
        api_key="YOUR-API-KEY",
        api_key_id="YOUR-API-KEY-ID"
    )
    robot = await RobotClient.at_address("YOUR-MACHINE-ADDRESS", opts)

servo = Servo.from_robot(robot, "pan-servo")

# Sweep through positions
    for angle in [0, 45, 90, 135, 180]:
        await servo.move(angle)
        current = await servo.get_position()
        print(f"Moved to {angle}°, position reads {current}°")
        await asyncio.sleep(0.5)

# Return to center
    await servo.move(90)
    print("Returned to 90°")

await robot.close()

if __name__ == "__main__":
    asyncio.run(main())
```

Run it:

```bash
python servo_test.py
```

Save this as `main.go`:

```go
package main

import (
    "context"
    "fmt"
    "time"

"go.viam.com/rdk/components/servo"
    "go.viam.com/rdk/logging"
    "go.viam.com/rdk/robot/client"
)

func main() {
    ctx := context.Background()
    logger := logging.NewLogger("servo-test")

robot, err := client.New(ctx, "YOUR-MACHINE-ADDRESS", logger,
        client.WithDialOptions(client.WithEntityCredentials(
            "YOUR-API-KEY-ID",
            client.Credentials{
                Type:    client.CredentialsTypeAPIKey,
                Payload: "YOUR-API-KEY",
            })),
    )
    if err != nil {
        logger.Fatal(err)
    }
    defer robot.Close(ctx)

s, err := servo.FromProvider(robot, "pan-servo")
    if err != nil {
        logger.Fatal(err)
    }

// Sweep through positions
    for _, angle := range []uint32{0, 45, 90, 135, 180} {
        if err := s.Move(ctx, angle, nil); err != nil {
            logger.Fatal(err)
        }
        position, err := s.Position(ctx, nil)
        if err != nil {
            logger.Fatal(err)
        }
        fmt.Printf("Moved to %d°, position reads %d°\n", angle, position)
        time.Sleep(500 * time.Millisecond)
    }

// Return to center
    if err := s.Move(ctx, 90, nil); err != nil {
        logger.Fatal(err)
    }
    fmt.Println("Returned to 90°")
}
```

Run it:

```bash
go run main.go
```

## Troubleshooting

##### Servo jitters or buzzes

- The servo may not be getting enough power. Servos draw significant current when loaded. Use an external power supply rather than powering from the SBC’s GPIO header.
- Adjust `frequency_hz`. Some servos work better at 50 Hz, others at 300 Hz.

##### Servo doesn't reach full range

- Adjust `min_width_us` and `max_width_us` to match your servo’s actual pulse width range. Check the servo’s datasheet for the correct values.

##### Servo doesn't respond

- Verify the pin supports PWM output. Not all GPIO pins can generate PWM.
- Check power and ground connections.
- Confirm the pin number in your config matches the physical wiring.

If your servo is not working as expected, follow these steps:

1. Check your machine logs on the **LOGS** tab to check for errors.
2. Review this servo model’s documentation to ensure you have configured all required attributes.
3. Check that all wires are securely attached to the correct pins on the board.
4. Click on the **TEST** panel on the **CONFIGURE** or **CONTROL** tab and test if you can use the servo there.

If none of these steps work, reach out to us on the [Community Discord](https://discord.gg/viam) and we will be happy to help.
