Go to Try Viam and borrow a rover.
If a rover is available, the rover will take up to 30 seconds to be configured for you.
Tip
If you are running out of time during your session, you can extend your rover session as long as there are no other reservations.
Important
If you are using your own robot for this tutorial instead of borrowing one, be sure to install viam-server on it, and connect and configure its hardware before proceeding with this tutorial.
Step 2: Install an SDK
Navigate to your machine’s CONNECT tab.
Click on any of the listed languages and follow the instructions to install the SDK.
To install your preferred Viam SDK on your Linux or macOS development machine or single-board computer, run one of the following commands in your terminal:
If you are using the Python SDK, set up a virtual environment to package the SDK inside before running your code, avoiding conflicts with other projects or your system.
For macOS (both Intel x86_64 and Apple Silicon) or Linux (x86, aarch64, armv6l), run the following commands:
Windows is not supported.
If you are using Windows, use the Windows Subsystem for Linux (WSL) and install the Python SDK using the preceding instructions for Linux.
For other unsupported systems, see Installing from source.
The sample code on the CONNECT tab will show you how to authenticate and connect to a machine, as well as some of the methods you can use on your configured components and services.
Go to the CONNECT tab and select Python.
Save your API key and API key ID as environment variables or include them in the code:
API key and API key ID
By default, the sample code does not include your machine API key and API key ID.
We strongly recommend that you add your API key and API key ID as an environment variable and import this variable into your development environment as needed.
To show your machine’s API key and API key ID in the sample code, toggle Include API key on the CONNECT tab’s Code sample page.
Caution
Do not share your API key or machine address publicly.
Sharing this information could compromise your system security by allowing unauthorized access to your machine, or to the computer running your machine.
Copy the code into a file called square.py and run the sample code to connect to your machine:
python3 square.py
The program prints an array of resources.
These are the components and services that the machine is configured with in the Viam app.
python3 square.py
2024-08-09 13:21:52,423 INFO viam.rpc.dial (dial.py:293) Connecting to socket: /tmp/proxy-BzFWLZQ2.sock
Resources:
[<viam.proto.common.ResourceName rdk:service:sensors/builtin at 0x105b12700>, <viam.proto.common.ResourceName rdk:component:motor/left at 0x105b122a0>, <viam.proto.common.ResourceName rdk:component:camera/cam at 0x105b12390>, <viam.proto.common.ResourceName rdk:component:board/local at 0x105b129d0>, <viam.proto.common.ResourceName rdk:component:base/viam_base at 0x105b12610>, <viam.proto.common.ResourceName rdk:service:motion/builtin at 0x105b12a20>, <viam.proto.common.ResourceName rdk:component:encoder/Lenc at 0x105b12a70>, <viam.proto.common.ResourceName rdk:component:motor/right at 0x105b12ac0>, <viam.proto.common.ResourceName rdk:component:encoder/Renc at 0x105b12b10>]
Go to the CONNECT tab and select Golang.
Save your API key and API key ID as environment variables or include them in the code:
API key and API key ID
By default, the sample code does not include your machine API key and API key ID.
We strongly recommend that you add your API key and API key ID as an environment variable and import this variable into your development environment as needed.
To show your machine’s API key and API key ID in the sample code, toggle Include API key on the CONNECT tab’s Code sample page.
Caution
Do not share your API key or machine address publicly.
Sharing this information could compromise your system security by allowing unauthorized access to your machine, or to the computer running your machine.
Copy the code into a file called square.go.
Initialize your project, and install the necessary libraries, and then run the program to verify that the Viam SDK is properly installed and that the viam-server instance on your machine is live:
go mod init square
go mod tidy
go run square.go
The program prints an array of resources.
These are the components and services that the machine is configured with in the Viam app.
go run square.go
2024-08-09T11:27:05.034Z DEBUG client.webrtc rpc/wrtc_client.go:111 connecting to signaling server {"signaling_server":"app.viam.com:443","host":"muddy-snow-main.7kp7y4p393.viam.cloud"}
2024-08-09T11:27:05.314Z DEBUG client.webrtc rpc/wrtc_client.go:125 connected to signaling server {"signaling_server":"app.viam.com:443"}
...
2024-08-09T11:27:08.916Z INFO client test-rover/square.go:33 Resources:
2024-08-09T11:27:08.916Z INFO client test-rover/square.go:34 [rdk:component:board/local rdk:component:base/viam_base rdk:service:motion/builtin rdk:component:motor/right rdk:component:encoder/Renc rdk:component:encoder/Lenc rdk:service:sensors/builtin rdk:component:motor/left rdk:component:camera/cam]
Fast track
If you want to get the full sample code now, run the following command which will set up the files for you:
npm create robot-app
Then, copy and paste your API key and API key ID, and machine address into the .env file.
If you have run this command and added your environment variables, skip ahead to running the program.
Go to the CONNECT tab and select TypeScript.
Save your API key and API key ID as environment variables or include them in the code:
API key and API key ID
By default, the sample code does not include your machine API key and API key ID.
We strongly recommend that you add your API key and API key ID as an environment variable and import this variable into your development environment as needed.
To show your machine’s API key and API key ID in the sample code, toggle Include API key on the CONNECT tab’s Code sample page.
Caution
Do not share your API key or machine address publicly.
Sharing this information could compromise your system security by allowing unauthorized access to your machine, or to the computer running your machine.
Copy the code into a file called main.ts.
Create another file named package.json with the following contents:
Create a folder static and inside it another file named index.html.
Add the following markup:
<!doctype html>
<html>
<head>
<title>Drive a Rover</title>
<link rel="icon" href="favicon.ico" />
</head>
<body>
<div id="main">
<h1>Drive a rover in a square</h1>
<p>
We recommend you open the developer tools in your browser to see logs.
</p>
<p>
Also open a second window with the
<a href="https://app.viam.com">Viam app</a> and navigate to your rover's
<b>CONTROL</b> tab, which allows you to interact with your rover's
resources. Click on one of the camera panels and toggle the camera
stream on so you can observe the rover's movements.
</p>
<button id="main-button" disabled="true">
Click me to drive rover in square
</button>
</div>
<script type="module" src="main.js"></script>
</body>
</html>
Run the following commands to install the necessary libraries, and then run the program to verify that the Viam SDK is properly installed and that the viam-server instance on your machine is live:
npm install
npm start
Open a web browser and visit localhost:8000.
You should see a disabled button that says Click me to drive rover in square.
Open the developer console to see the console output.
If you successfully configured your machine and it is able to connect to the Viam app, you will see some output including the names of your rover’s resources.
These are the components and services that the machine is configured with in the Viam app.
Flutter code must be launched from inside a running Flutter application.
To get started programming your rover with Flutter, follow the instructions to Build a Flutter App that Integrates with Viam.
Go to the CONNECT tab and select C++.
Save your API key and API key ID as environment variables or include them in the code:
API key and API key ID
By default, the sample code does not include your machine API key and API key ID.
We strongly recommend that you add your API key and API key ID as an environment variable and import this variable into your development environment as needed.
To show your machine’s API key and API key ID in the sample code, toggle Include API key on the CONNECT tab’s Code sample page.
Caution
Do not share your API key or machine address publicly.
Sharing this information could compromise your system security by allowing unauthorized access to your machine, or to the computer running your machine.
Copy the code into a file called drive_in_square.cpp.
Compile your code, and then run the program to verify that the Viam SDK is properly installed and that the viam-server instance on your machine is live:
cmake . -G Ninja
ninja all
./drive_in_square
The program prints an array of resources.
These are the components and services that the machine is configured with in the Viam app.
The base is responsible for controlling the motors attached to the base of the rover.
Ensure that the Base class is being imported:
from viam.components.base import Base
Then paste this snippet above your main() function, it will move any base passed to it in a square:
async def moveInSquare(base):
for _ in range(4):
# moves the rover forward 500mm at 500mm/s
await base.move_straight(velocity=500, distance=500)
print("move straight")
# spins the rover 90 degrees at 100 degrees per second
await base.spin(velocity=100, angle=90)
print("spin 90 degrees")
Next, remove all the code in the main() function between where the machine connection is established and closed and instead initialize your base and invoke the moveInSquare() function.
On the Try Viam rovers, the default base name is viam_base.
If you have a different base name, update the name in your code.
async def main():
machine = await connect()
# Get the base component from the rover
roverBase = Base.from_robot(machine, 'viam_base')
# Move the rover in a square
await moveInSquare(roverBase)
await machine.close()
If you have a borrowed Try Viam rover, navigate to your machine’s CONTROL tab, which allows you to interact with your machine’s resources.
Click on one of the camera panels and toggle the camera stream on so you can observe the rover’s movements.
Then run your code and watch your rover move in a square.
The base is responsible for controlling the motors attached to the base of the rover.
Ensure that the Base class is being imported:
import (
// Be sure to keep all of the other imported libraries
"go.viam.com/rdk/components/base"
)
Then paste this snippet above your main() function, it will move any base passed to it in a square:
func moveInSquare(ctx context.Context, base base.Base, logger logging.Logger) {
for i := 0; i < 4; i++ {
// moves the rover forward 600mm at 500mm/s
base.MoveStraight(ctx, 600, 500.0, nil)
logger.Info("move straight")
// spins the rover 90 degrees at 100 degrees per second
base.Spin(ctx, 90, 100.0, nil)
logger.Info("spin 90 degrees")
}
}
Next, remove all the code in the main() function after the machine connection is established and instead initialize your base and invoke the moveInSquare() function.
Also remove any unused imports.
On the Try Viam rovers, the default base name is viam_base.
If you have a different base name, update the name in your code.
func main() {
logger := logging.NewLogger("client")
machine, err := client.New(
context.Background(),
"ADDRESS FROM THE VIAM APP",
logger,
client.WithDialOptions(rpc.WithEntityCredentials(
/* Replace "<API-KEY-ID>" (including brackets) with your machine's api key id */
"<API-KEY-ID>",
rpc.Credentials{
Type: rpc.CredentialsTypeAPIKey,
/* Replace "<API-KEY>" (including brackets) with your machine's api key */
Payload: "<API-KEY>",
})),
)
if err != nil {
logger.Fatal(err)
}
defer machine.Close(context.Background())
// Get the base from the rover
roverBase, err := base.FromRobot(machine, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
// Move the rover in a square
moveInSquare(context.Background(), roverBase, logger)
}
If you have a borrowed Try Viam rover, navigate to your machine’s CONTROL tab, which allows you to interact with your machine’s resources.
Click on one of the camera panels and toggle the camera stream on so you can observe the rover’s movements.
Then run your code and watch your rover move in a square.
Above the main function, add the following function that initializes your rover base client and drives it in a square.
On the Try Viam rovers, the default base name is viam_base.
If you have a different base name, update the name in your code.
// This function moves a base component in a square.
async function moveInSquare(client: VIAM.RobotClient) {
// Replace with the name of the base on your machine.
const name = "viam_base";
const baseClient = new VIAM.BaseClient(client, name);
try {
button().disabled = true;
for (let i = 0; i < 4; i++) {
console.log("move straight");
await baseClient.moveStraight(500, 500);
console.log("spin 90 degrees");
await baseClient.spin(90, 100);
}
} finally {
button().disabled = false;
}
}
Underneath the moveInSquare function, add this button function which gets the main-button button from the page loaded from index.html:
// This function gets the button element
function button() {
return <HTMLButtonElement>document.getElementById("main-button");
}
Next, register a listener on the button you obtain from the button function and make it invoke the moveInSquare function.
Place this code after the rover connection code:
async function main() {
const host = "ADDRESS_FROM_VIAM_APP";
const machine = await VIAM.createRobotClient({
host,
credential: {
type: "api-key",
// Replace "<API-KEY>" (including brackets) with your machine's API key
payload: "<API-KEY>",
},
// Replace "<API-KEY-ID>" (including brackets) with your machine's API key ID
authEntity: "<API-KEY-ID>",
signalingAddress: "https://app.viam.com:443",
});
button().onclick = async () => {
await moveInSquare(machine);
};
button().disabled = false;
}
Save your code and refresh the browser page.
When the connection to the rover is established, the Click me to drive rover in square button will become enabled.
If you have a borrowed Try Viam rover, open a tab and navigate to your machine’s CONTROL tab, which allows you to interact with your rover’s resources.
Click on one of the camera panels and toggle the camera stream on so you can observe the rover’s movements.
Go back to the Click me to drive rover in square button and click the button to run your code and switch back to the CONTROL tab to watch your rover move in a square.
Add a new file to your application in /lib called base_screen.dart.
Paste this code into your file:
/// This is the BaseScreen, which allows us to control a Base.
import 'package:flutter/material.dart';
import 'package:viam_sdk/viam_sdk.dart';
import 'package:viam_sdk/widgets.dart';
class BaseScreen extends StatelessWidget {
final Base base;
const BaseScreen(this.base, {super.key});
Future<void> moveSquare() async {
for (var i=0; i<4; i++) {
await base.moveStraight(500, 500);
await base.spin(90, 100);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(base.name)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: moveSquare,
child: const Text('Move Base in Square'),
),
]))
,);}}
This code creates a screen with a singular centered button that, when pressed, calls on the moveSquare() method to drive the base in a square.
Then, replace the contents of robot_screen.dart with the following file, or add the highlighted lines of code to your program in the locations indicated:
/// This is the screen that shows the resources available on a robot (or smart machine).
/// It takes in a Viam app client instance, as well as a robot client.
/// It then uses the Viam client instance to create a connection to that robot client.
/// Once the connection is established, you can view the resources available
/// and send commands to them.
import 'package:flutter/material.dart';
import 'package:viam_sdk/protos/app/app.dart';
import 'package:viam_sdk/viam_sdk.dart';
import 'base_screen.dart';
class RobotScreen extends StatefulWidget {
final Viam _viam;
final Robot robot;
const RobotScreen(this._viam, this.robot, {super.key});
@override
State<RobotScreen> createState() => _RobotScreenState();
}
class _RobotScreenState extends State<RobotScreen> {
/// Similar to previous screens, start with [_isLoading] to true.
bool _isLoading = true;
/// This is the [RobotClient], which allows you to access
/// all the resources of a Viam Smart Machine.
/// This differs from the [Robot] provided to us in the widget constructor
/// in that the [RobotClient] contains a direct connection to the Smart Machine
/// and its resources. The [Robot] object simply contains information about
/// the Smart Machine, but is not actually connected to the machine itself.
///
/// This is initialized late because it requires an asynchronous
/// network call to establish the connection.
late RobotClient client;
@override
void initState() {
super.initState();
// Call our own _initState method to initialize our state.
_initState();
}
@override
void dispose() {
// You should always close the [RobotClient] to free up resources.
// Calling [RobotClient.close] will clean up any tasks and
// resources created by Viam.
if (_isLoading == false) {
client.close();
}
super.dispose();
}
/// This method will get called when the widget initializes its state.
/// It exists outside the overridden [initState] function since it's async.
Future<void> _initState() async {
// Using the authenticated [Viam] the received as a parameter,
// the app can obtain a connection to the Robot.
// There is a helpful convenience method on the [Viam] instance for this.
final robotClient = await widget._viam.getRobotClient(widget.robot);
setState(() {
client = robotClient;
_isLoading = false;
});
}
/// A computed variable that returns the available [ResourceName]s of
/// this robot in an alphabetically sorted list.
List<ResourceName> get _sortedResourceNames {
return client.resourceNames..sort((a, b) => a.name.compareTo(b.name));
}
bool _isNavigable(ResourceName rn) {
if (rn.subtype == Base.subtype.resourceSubtype) {
return true;
}
return false;
}
void _navigate(ResourceName rn) {
if (rn.subtype == Base.subtype.resourceSubtype) {
final base = Base.fromRobot(client, rn.name);
Navigator.of(context).push(MaterialPageRoute(builder: (_) => BaseScreen(base)));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.robot.name)),
body: _isLoading
? const Center(child: CircularProgressIndicator.adaptive())
: ListView.builder(
itemCount: client.resourceNames.length,
itemBuilder: (_, index) {
final resourceName = _sortedResourceNames[index];
return ListTile(
title: Text(resourceName.name),
subtitle: Text(
'${resourceName.namespace}:${resourceName.type}:${resourceName.subtype}'),
onTap: () => _navigate(resourceName),
trailing: _isNavigable(resourceName) ? Icon(Icons.chevron_right) : SizedBox.shrink(),
);
}));
}
}
This imports the base_screen.dart file into the program and adds logic to check if a resource is “navigable”, or, has a screen made for it.
Base is the only resource that is navigable.
To navigate to the base screen, save your code and launch your simulator.
Navigate to the robot screen of a (live) machine with a base resource configured, and see the resources displayed like the following:
Then, click on the base to display the base screen.
You may need to scroll to the bottom of the list of resources.
Click on the button to move your rover in a square:
The base is responsible for controlling the motors attached to the base of the rover.
Ensure that the Base class is being imported:
#include <viam/sdk/components/base.hpp>
Additionally, add the following namespaces under your imports:
using namespace viam::sdk;
using std::cerr;
using std::cout;
using std::endl;
Then paste this snippet above your main() function, it will move any base passed to it in a square:
void move_in_square(std::shared_ptr<viam::sdk::Base> base) {
for (int i = 0; i < 4; ++i) {
cout << "Move straight" << endl;
// Move the base forward 600mm at 500mm/s
base->move_straight(500, 500);
cout << "Spin" << endl;
// Spin the base by 90 degree at 100 degrees per second
base->spin(90, 100);
}
}
Next, remove all the code in the main() function after the machine connection is established, and instead initialize your base and invoke the moveInSquare() function.
On the Try Viam rovers, the default base name is viam_base.
If you have a different base name, update the name in your code.
int main() {
std::string host("ADDRESS FROM THE VIAM APP");
DialOptions dial_opts;
// Replace "<API-KEY-ID>" with your machine's api key ID
dial_opts.set_entity(std::string("<API-KEY-ID>"));
// Replace "<API-KEY>" with your machine's api key
Credentials credentials("api-key", "<API-KEY>");
dial_opts.set_credentials(credentials);
boost::optional<DialOptions> opts(dial_opts);
Options options(0, opts);
auto machine = RobotClient::at_address(host, options);
std::cout << "Resources:\n";
for (const Name& resource : machine->resource_names()) {
std::cout << "\t" << resource << "\n";
}
std::string base_name("viam_base");
cout << "Getting base: " << base_name << endl;
std::shared_ptr<Base> base;
try {
base = machine->resource_by_name<Base>(base_name);
move_in_square(base);
} catch (const std::exception& e) {
cerr << "Failed to find " << base_name << ". Exiting." << endl;
throw;
}
return EXIT_SUCCESS;
}
If you have a borrowed Try Viam rover, navigate to your machine’s CONTROL tab, which allows you to interact with your machine’s resources.
Click on one of the camera panels and toggle the camera stream on so you can observe the rover’s movements.
Then run your code and watch your rover move in a square.
Tip
If you are interested to learn about what other commands you can give to a base, see the standardized base API for a full list of available API methods.
import asyncio
from viam.components.base import Base
from viam.robot.client import RobotClient
from viam.rpc.dial import Credentials, DialOptions
async def connect():
opts = RobotClient.Options.with_api_key(
# Replace "<API-KEY>" (including brackets) with your machine's API key
api_key='<API-KEY>',
# Replace "<API-KEY-ID>" (including brackets) with your machine's
# API key ID
api_key_id='<API-KEY-ID>'
)
return await RobotClient.at_address('ADDRESS FROM THE VIAM APP', opts)
async def moveInSquare(base):
for _ in range(4):
# moves the rover forward 500mm at 500mm/s
await base.move_straight(velocity=500, distance=500)
print("move straight")
# spins the rover 90 degrees at 100 degrees per second
await base.spin(velocity=100, angle=90)
print("spin 90 degrees")
async def main():
machine = await connect()
roverBase = Base.from_robot(machine, 'viam_base')
# Move the rover in a square
await moveInSquare(roverBase)
await machine.close()
if __name__ == '__main__':
asyncio.run(main())
package main
import (
"context"
"go.viam.com/rdk/components/base"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/robot/client"
"go.viam.com/rdk/utils")
func moveInSquare(ctx context.Context, base base.Base, logger logging.Logger) {
for i := 0; i < 4; i++ {
// moves the rover forward 600mm at 500mm/s
base.MoveStraight(ctx, 600, 500.0, nil)
logger.Info("move straight")
// spins the rover 90 degrees at 100 degrees per second
base.Spin(ctx, 90, 100.0, nil)
logger.Info("spin 90 degrees")
}
}
func main() {
logger := logging.NewLogger("client")
machine, err := client.New(
context.Background(),
"ADDRESS FROM THE VIAM APP",
logger,
client.WithDialOptions(utils.WithEntityCredentials(
// Replace "<API-KEY-ID>" (including brackets) with your machine's API key ID
"<API-KEY-ID>",
utils.Credentials{
Type: utils.CredentialsTypeAPIKey,
// Replace "<API-KEY>" (including brackets) with your machine's API key
Payload: "<API-KEY>",
})),
)
if err != nil {
logger.Fatal(err)
}
defer machine.Close(context.Background())
// Get the base from the rover
roverBase, err := base.FromRobot(machine, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
// Move the rover in a square
moveInSquare(context.Background(), roverBase, logger)
}
<!doctype html>
<html>
<head>
<title>Drive a Rover</title>
<link rel="icon" href="favicon.ico" />
</head>
<body>
<div id="main">
<h1>Drive a rover in a square</h1>
<p>
We recommend you open the developer tools in your browser to see logs.
</p>
<p>
Also open a second window with the
<a href="https://app.viam.com">Viam app</a> and navigate to your rover's
<b>CONTROL</b> tab, which allows you to interact with your rover's
resources. Click on one of the camera panels and toggle the camera
stream on so you can observe the rover's movements.
</p>
<button id="main-button" disabled="true">
Click me to drive rover in square
</button>
</div>
<script type="module" src="main.js"></script>
</body>
</html>
main.ts:
// This code must be run in a browser environment.
import * as VIAM from "@viamrobotics/sdk";
// This function moves a base component in a square.
async function moveInSquare(client: VIAM.RobotClient) {
// Replace with the name of the base on your machine.
const name = "viam_base";
const baseClient = new VIAM.BaseClient(client, name);
try {
button().disabled = true;
for (let i = 0; i < 4; i++) {
console.log("move straight");
await baseClient.moveStraight(500, 500);
console.log("spin 90 degrees");
await baseClient.spin(90, 100);
}
} finally {
button().disabled = false;
}
}
// This function gets the button element
function button() {
return <HTMLButtonElement>document.getElementById("main-button");
}
const main = async () => {
const host = "ADDRESS_FROM_VIAM_APP";
const machine = await VIAM.createRobotClient({
host,
credential: {
type: "api-key",
// Replace "<API-KEY>" (including brackets) with your machine's API key
payload: "<API-KEY>",
},
// Replace "<API-KEY-ID>" (including brackets) with your machine's API key ID
authEntity: "<API-KEY-ID>",
signalingAddress: "https://app.viam.com:443",
});
button().onclick = async () => {
await moveInSquare(machine);
};
button().disabled = false;
};
main().catch((error) => {
console.error("encountered an error:", error);
});
robot_screen.dart:
/// This is the BaseScreen, which allows us to control a Base.
import 'package:flutter/material.dart';
import 'package:viam_sdk/viam_sdk.dart';
import 'package:viam_sdk/widgets.dart';
class BaseScreen extends StatelessWidget {
final Base base;
const BaseScreen(this.base, {super.key});
Future<void> moveSquare() async {
for (var i=0; i<4; i++) {
await base.moveStraight(500, 500);
await base.spin(90, 100);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(base.name)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: moveSquare,
child: const Text('Move Base in Square'),
),
]))
,);}}
base_screen.dart:
/// This is the screen that shows the resources available on a robot (or smart machine).
/// It takes in a Viam app client instance, as well as a robot client.
/// It then uses the Viam client instance to create a connection to that robot client.
/// Once the connection is established, you can view the resources available
/// and send commands to them.
import 'package:flutter/material.dart';
import 'base_screen.dart';
import 'package:viam_sdk/protos/app/app.dart';
import 'package:viam_sdk/viam_sdk.dart';
class RobotScreen extends StatefulWidget {
final Viam _viam;
final Robot robot;
const RobotScreen(this._viam, this.robot, {super.key});
@override
State<RobotScreen> createState() => _RobotScreenState();
}
class _RobotScreenState extends State<RobotScreen> {
/// Similar to previous screens, start with [_isLoading] to true.
bool _isLoading = true;
/// This is the [RobotClient], which allows you to access
/// all the resources of a Viam Smart Machine.
/// This differs from the [Robot] provided to us in the widget constructor
/// in that the [RobotClient] contains a direct connection to the Smart Machine
/// and its resources. The [Robot] object simply contains information about
/// the Smart Machine, but is not actually connected to the machine itself.
///
/// This is initialized late because it requires an asynchronous
/// network call to establish the connection.
late RobotClient client;
@override
void initState() {
super.initState();
// Call our own _initState method to initialize our state.
_initState();
}
@override
void dispose() {
// You should always close the [RobotClient] to free up resources.
// Calling [RobotClient.close] will clean up any tasks and
// resources created by Viam.
client.close();
super.dispose();
}
/// This method will get called when the widget initializes its state.
/// It exists outside the overridden [initState] function since it's async.
Future<void> _initState() async {
// Using the authenticated [Viam] the received as a parameter,
// the app can obtain a connection to the Robot.
// There is a helpful convenience method on the [Viam] instance for this.
final robotClient = await widget._viam.getRobotClient(widget.robot);
setState(() {
client = robotClient;
_isLoading = false;
});
}
/// A computed variable that returns the available [ResourceName]s of
/// this robot in an alphabetically sorted list.
List<ResourceName> get _sortedResourceNames {
return client.resourceNames..sort((a, b) => a.name.compareTo(b.name));
}
bool _isNavigable(ResourceName rn) {
if (rn.subtype == Base.subtype.resourceSubtype) {
return true;
}
return false;
}
void _navigate(ResourceName rn) {
if (rn.subtype == Base.subtype.resourceSubtype) {
final base = Base.fromRobot(client, rn.name);
Navigator.of(context).push(MaterialPageRoute(builder: (_) => BaseScreen(base)));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.robot.name)),
body: _isLoading
? const Center(child: CircularProgressIndicator.adaptive())
: ListView.builder(
itemCount: client.resourceNames.length,
itemBuilder: (_, index) {
final resourceName = _sortedResourceNames[index];
return ListTile(
title: Text(resourceName.name),
subtitle: Text(
'${resourceName.namespace}:${resourceName.type}:${resourceName.subtype}'),
onTap: () => _navigate(resourceName),
trailing: _isNavigable(resourceName) ? Icon(Icons.chevron_right) : SizedBox.shrink(),
);
}));
}
}
#include <boost/optional.hpp>
#include <string>
#include <vector>
#include <viam/sdk/robot/client.hpp>
#include <viam/sdk/components/motor.hpp>
#include <viam/sdk/components/base.hpp>
#include <viam/sdk/components/camera.hpp>
#include <viam/sdk/components/encoder.hpp>
using namespace viam::sdk;
using namespace viam::sdk;
using std::cerr;
using std::cout;
using std::endl;
void move_in_square(std::shared_ptr<viam::sdk::Base> base) {
for (int i = 0; i < 4; ++i) {
cout << "Move straight" << endl;
// Move the base forward 600mm at 500mm/s
base->move_straight(500, 500);
cout << "Spin" << endl;
// Spin the base by 90 degree at 100 degrees per second
base->spin(90, 100);
}
}
int main() {
std::string host("ADDRESS FROM THE VIAM APP");
DialOptions dial_opts;
// Replace "<API-KEY-ID>" with your machine's api key ID
dial_opts.set_entity(std::string("<API-KEY-ID>"));
// Replace "<API-KEY>" with your machine's api key
Credentials credentials("api-key", "<API-KEY>");
dial_opts.set_credentials(credentials);
boost::optional<DialOptions> opts(dial_opts);
Options options(0, opts);
auto machine = RobotClient::at_address(host, options);
std::cout << "Resources:\n";
for (const Name& resource : machine->resource_names()) {
std::cout << "\t" << resource << "\n";
}
std::string base_name("viam_base");
cout << "Getting base: " << base_name << endl;
std::shared_ptr<Base> base;
try {
base = machine->resource_by_name<Base>(base_name);
move_in_square(base);
} catch (const std::exception& e) {
cerr << "Failed to find " << base_name << ". Exiting." << endl;
throw;
}
return EXIT_SUCCESS;
}
Next steps
Now that you have run your first code to control a machine running Viam code to control your machine, move to the next quickstart to learn how to configure and control a motor: