Add an encoder | Add a component

Add an encoder

Add an encoder to your machine’s configuration so you can track motor position and direction from the Viam app and from code.

Concepts

Encoders come in two main varieties:

Both connect to a board’s GPIO pins as digital interrupts. The encoder counts signal transitions (“ticks”) that correspond to motor shaft rotation.

Once you configure an encoder and reference it from a motor component, the motor gains accurate position control. GoFor and GoTo use actual encoder feedback instead of time-based estimates. Search for encoder in the Viam registry to see available models.

Built-in models

Micro-RDK:

Registry modules

Viam-maintained encoder modules:

Module Encoders supported
viam:ams AMS AS5048 magnetic rotary absolute encoder

For encoders not covered above, search for encoder in the Viam registry.

Steps

1. Prerequisites

2. Add an encoder component

  1. Click the + button.
  2. Select Blocks.
  3. Search for the encoder model that matches your hardware:
    • For a two-channel quadrature encoder, search for incremental.
    • For a single-channel encoder, search for single encoder.
  4. Name your encoder (for example, left-encoder) and click Add to machine.

3. Configure encoder attributes

Incremental (quadrature) encoder:

{
  "board": "my-board",
  "pins": {
    "a": "37",
    "b": "35"
  }
}
Attribute Type Required Description
board string Yes Name of the board component.
pins.a string Yes GPIO pin for channel A.
pins.b string Yes GPIO pin for channel B.

Single-channel encoder:

{
  "board": "my-board",
  "pins": {
    "i": "37"
  }
}
Attribute Type Required Description
board string Yes Name of the board component.
pins.i string Yes GPIO pin for the signal channel.

4. Link the encoder to a motor

After creating the encoder, update your motor’s configuration to reference it:

{
  "board": "my-board",
  "max_rpm": 200,
  "pins": { "a": "11", "b": "13", "pwm": "15" },
  "encoder": "left-encoder",
  "ticks_per_rotation": 600
}

ticks_per_rotation is the number of encoder ticks in one full rotation of the motor shaft. Check your encoder’s datasheet. Common values are 12, 48, 200, or 600 depending on the encoder type and any gearing.

5. Save and test

Click Save, then expand the Test section for the encoder.

Then test the motor. GoFor should now stop accurately at the target position.

Try it

Read the encoder position and reset it.

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. When you run the code below, you’ll see the encoder’s current position, then reset it to zero. Manually rotate the motor shaft to verify the count changes.

Install the Viam Python SDK in a virtual environment by following Install the Python SDK.

Save this as encoder_test.py:

import asyncio
from viam.robot.client import RobotClient
from viam.components.encoder import Encoder

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)

encoder = Encoder.from_robot(robot, "left-encoder")

position, pos_type = await encoder.get_position()
    print(f"Position: {position} ({pos_type})")

properties = await encoder.get_properties()
    print(f"Ticks count supported: {properties.ticks_count_supported}")
    print(f"Angle degrees supported: {properties.angle_degrees_supported}")

await encoder.reset_position()
    print("Position reset to zero")

position, pos_type = await encoder.get_position()
    print(f"Position after reset: {position}")

await robot.close()

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

Run it:

python encoder_test.py

Save this as main.go:

package main

import (
    "context"
    "fmt"

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

func main() {
    ctx := context.Background()
    logger := logging.NewLogger("encoder-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)

enc, err := encoder.FromProvider(robot, "left-encoder")
    if err != nil {
        logger.Fatal(err)
    }

position, posType, err := enc.Position(ctx, encoder.PositionTypeUnspecified, nil)
    if err != nil {
        logger.Fatal(err)
    }
    fmt.Printf("Position: %.2f (%v)\n", position, posType)

properties, err := enc.Properties(ctx, nil)
    if err != nil {
        logger.Fatal(err)
    }
    fmt.Printf("Ticks count supported: %v\n", properties.TicksCountSupported)
    fmt.Printf("Angle degrees supported: %v\n", properties.AngleDegreesSupported)

if err := enc.ResetPosition(ctx, nil); err != nil {
        logger.Fatal(err)
    }
    fmt.Println("Position reset to zero")
}

Run it:

go run main.go

Troubleshooting

Encoder count doesn't change
Count always increases (never decreases)
Position is inaccurate or drifts

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

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

If none of these steps work, reach out to us on the Community Discord and we will be happy to help.