Add an input controller
Add an input controller to your machine’s configuration so you can use a gamepad, joystick, or other input device to control your machine.
Concepts
An input controller component reads events from human input devices: button presses, joystick axis movements, and trigger pulls. Your code registers callbacks for these events and translates them into machine actions.
Built-in models
| Model | Use case |
|---|---|
gamepad | USB gamepad or joystick. Works with most HID-compatible controllers. |
webgamepad | Browser-based gamepad input in the Viam app’s CONTROL tab. No physical controller needed. |
gpio | Buttons and switches wired to GPIO pins on a board. |
mux | Multiplexes multiple input controllers, allowing one to override another (for example, safety controller overrides gamepad). |
Browse all available input controller models in the Viam registry.
Steps
Option A: USB gamepad
1. Add an input controller component
- Plug your gamepad into the machine’s USB port.
- Click the + button.
- Select Configuration block.
- Search for gamepad. This is the built-in model for USB game controllers.
- Name it (for example,
my-gamepad) and click Create.
2. Configure attributes
The gamepad model typically needs no attributes. It auto-detects the
connected controller.
If you have multiple controllers, specify which one:
{
"dev_file": "/dev/input/event0"
}
Option B: Web gamepad
1. Add a webgamepad component
- Click the + button.
- Select Configuration block.
- Search for webgamepad. This model provides browser-based controls in the Viam app.
- Name it (for example,
web-controller) and click Create.
No attributes needed. The web gamepad appears in the Viam app’s CONTROL tab and works with browser-compatible game controllers or on-screen controls.
Option C: GPIO buttons
1. Prerequisites
- A board component is configured.
- Buttons or switches are wired to GPIO pins.
2. Add and configure
- Click the + button.
- Select Configuration block.
- Search for gpio input controller. This model maps GPIO pins to controller buttons.
- Name it and click Create.
- Configure the pins:
{
"board": "my-board",
"buttons": {
"ButtonNorth": "11",
"ButtonSouth": "13"
}
}
Save and test
Click Save, then expand the Test section.
- The test panel shows all available controls (buttons, axes).
- Press buttons or move joystick axes. Events should appear in real time.
Try it
List available controls and print events as they happen.
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 API key and API key ID. Copy the machine address from the same tab. When you run the code below, press buttons on your gamepad and watch the events print in real time.
pip install viam-sdk
Save this as input_test.py:
import asyncio
from viam.robot.client import RobotClient
from viam.components.input import Controller, Control, EventType
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)
controller = Controller.from_robot(robot, "my-gamepad")
# List available controls
controls = await controller.get_controls()
print(f"Available controls: {controls}")
# Get current state of all events
events = await controller.get_events()
for control, event in events.items():
print(f" {control}: {event.value}")
# Register a callback for button presses
def handle_press(event):
print(f"Button {event.control} {event.event}: value={event.value}")
controller.register_control_callback(
Control.BUTTON_START,
[EventType.BUTTON_PRESS, EventType.BUTTON_RELEASE],
handle_press,
)
print("\nPress buttons on the gamepad (Ctrl+C to stop)...")
# Keep running to receive callbacks
await asyncio.sleep(30)
await robot.close()
if __name__ == "__main__":
asyncio.run(main())
Run it:
python input_test.py
Press buttons on your gamepad and watch the events print.
mkdir input-test && cd input-test
go mod init input-test
go get go.viam.com/rdk
Save this as main.go:
package main
import (
"context"
"fmt"
"time"
"go.viam.com/rdk/components/input"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/robot/client"
"go.viam.com/rdk/utils"
)
func main() {
ctx := context.Background()
logger := logging.NewLogger("input-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)
controller, err := input.FromProvider(robot, "my-gamepad")
if err != nil {
logger.Fatal(err)
}
// List available controls
controls, err := controller.Controls(ctx, nil)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("Available controls: %v\n", controls)
// Get current state
events, err := controller.Events(ctx, nil)
if err != nil {
logger.Fatal(err)
}
for control, event := range events {
fmt.Printf(" %s: %.2f\n", control, event.Value)
}
// Register a callback for the Start button
err = controller.RegisterControlCallback(
ctx,
input.ButtonStart,
[]input.EventType{input.ButtonPress, input.ButtonRelease},
func(ctx context.Context, event input.Event) {
fmt.Printf("Button %s %s: value=%.2f\n",
event.Control, event.Event, event.Value)
},
nil,
)
if err != nil {
logger.Fatal(err)
}
fmt.Println("\nPress buttons on the gamepad (waiting 30s)...")
time.Sleep(30 * time.Second)
}
Run it:
go run main.go
Troubleshooting
What’s next
- Input controller API reference: full method documentation.
- Add a Base: drive a mobile robot with your gamepad.
- What is a module?: write a module that translates gamepad input into machine actions.
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!