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. Unlike a motor, which spins continuously, a servo provides precise angular positioning through PWM control.
The servo component uses a single PWM-capable pin on a board component. Browse all available servo models in the Viam registry.
Steps
1. Prerequisites
- Your machine is online in the Viam app.
- A board component 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
- Click the + button.
- Select Configuration block.
- For a standard hobby servo controlled by a PWM pin on your board, search for gpio servo.
- Name your servo (for example,
pan-servo) and click Create.
3. Configure servo attributes
{
"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.
- Use the angle slider to move the servo to different positions.
- 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 SDK code sample. Toggle Include API key on. Copy the machine address, API key, and API key ID from the code sample. If you’re using real hardware, you’ll see the servo sweep through positions when you run the code below.
pip install viam-sdk
Save this as servo_test.py:
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:
python servo_test.py
mkdir servo-test && cd servo-test
go mod init servo-test
go get go.viam.com/rdk
Save this as main.go:
package main
import (
"context"
"fmt"
"time"
"go.viam.com/rdk/components/servo"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/robot/client"
"go.viam.com/rdk/utils"
)
func main() {
ctx := context.Background()
logger := logging.NewLogger("servo-test")
robot, err := client.New(ctx, "YOUR-MACHINE-ADDRESS", logger,
client.WithCredentials(utils.Credentials{
Type: utils.CredentialsTypeAPIKey,
Payload: "YOUR-API-KEY",
}),
client.WithAPIKeyID("YOUR-API-KEY-ID"),
)
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:
go run main.go
Troubleshooting
What’s next
- Servo API reference: full method documentation.
- Add a Motor: for continuous rotation instead of angular positioning.
- What is a module?: write code that moves the servo based on sensor readings.
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!