To write control logic for your machine that will run without a user interface, you can use the Python, Go, or C++ SDK.
The SDKs each include similar methods to hit Viam’s gRPC API endpoints.
Decide where to run your code
You can run your code directly on the machine’s single-board computer (SBC), or you can run it from a separate computer connected to the internet or to the same local network as your machine’s SBC or microcontroller.
On a separate computer
We recommend running your code on a laptop, desktop, or server if:
You are using computationally-intensive programs involving, for example, computer vision or motion planning, and
You have a stable internet connection
The client code will establish a connection to the instance of viam-server on your machine’s SBC over LAN or WAN.
On the machine itself
We recommend running your code on the SBC that directly controls your hardware if:
Your machines have intermittent or no network connectivity, or
You want to reduce latency, for example for running PID control loops, or
Your machine runs continuously (for example, an air quality sensor) and it is impractical to constantly run a client from your development computer
Install an SDK
Install your preferred Viam SDK on the Linux or macOS computer SBC where you plan to run the control script.
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.
If you intend to use the ML (machine learning) model service, use the following command instead, which installs additional required dependencies along with the Python SDK:
pip install 'viam-sdk[mlmodel]'
Run the following command to install the Viam Go SDK:
You can find sample connection code on each machine’s CONNECT tab in the Viam app.
Select your preferred Language to display a code snippet, with connection code as well as some calls to the APIs of the resources you’ve configured on your machine.
You can use the toggle to include the machine API key and API key ID, though we strongly recommend storing your API keys in environment variables to reduce the risk of accidentally sharing your API key and granting access to your machines.
If your code will connect to multiple machines or use Platform APIs you can create an API key with broader access.
Write your control code
For API reference including code snippets for each method, see Viam’s Client APIs.
Example code for moving a rover in a square
The following code moves a mobile robot base in a square using the base API.
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)
}
#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;
}
Example Python code for turning on a fan based on sensor readings
import asyncio
import os
from dotenv import load_dotenv
from viam.logging import getLogger
from viam.robot.client import RobotClient
from viam.components.sensor import Sensor
from viam.components.generic import Generic
load_dotenv()
LOGGER = getLogger(__name__)
robot_api_key = os.getenv('ROBOT_API_KEY') or ''
robot_api_key_id = os.getenv('ROBOT_API_KEY_ID') or ''
robot_address = os.getenv('ROBOT_ADDRESS') or ''
# Define the sensor and plug names from the Viam app CONFIGURE tab
sensor_name = os.getenv("SENSOR_NAME", "")
plug_name = os.getenv("PLUG_NAME", "")
async def connect():
opts = RobotClient.Options.with_api_key(
api_key=robot_api_key,
api_key_id=robot_api_key_id
)
return await RobotClient.at_address(robot_address, opts)
async def main():
machine = await connect()
pms_7003 = Sensor.from_robot(machine, sensor_name)
kasa_plug = Generic.from_robot(machine, plug_name)
# Define unhealthy thresholds
unhealthy_thresholds = {
'pm2_5_atm': 35.4,
'pm10_atm': 150
}
while True:
readings = await pms_7003.get_readings()
# Check if any of the PM values exceed the unhealthy thresholds
if any(readings.get(pm_type, 0) > threshold for pm_type,
threshold in unhealthy_thresholds.items()):
LOGGER.info('UNHEALTHY.')
await kasa_plug.do_command({"toggle_on": []})
else:
LOGGER.info('HEALTHY!')
await kasa_plug.do_command({"toggle_off": []})
# wait before checking again
await asyncio.sleep(10)
if __name__ == '__main__':
asyncio.run(main())
Run your code
You can run your code manually from your terminal, or you can automatically run it each time your machine starts.
Run your code manually
To run your code on a laptop or desktop, or to run it on your machine’s SBC manually for testing purposes, execute the following command in a terminal:
If you want your code to run each time your machine boots, configure the code as a process on your machine.
Configured processes are managed by viam-server and are a way to run any specified command either once on boot or continuously over the lifecycle of viam-server.
Tip
If you are running your code from a laptop or desktop, we do not recommend configuring your code to run as a process because doing so will cause the process to run whenever you boot your computer, even when you are using your computer for unrelated purposes.
To configure a process, click the + button on your machine’s CONFIGURE tab and select Process.
For detailed configuration information, see Configure a managed process.
Example
The following example executes the command python3 my_cool_script.py in your /home/myName/project/ directory every time your machine boots, and restarts the process if it stops running.