Create custom components and services as modular resources

The Viam module system allows you to integrate custom resources (components and services) into any robot running on Viam.

With modular resources, you can:

  • Create new models of built-in component or service types
  • Create brand new resource types

viam-server manages modular resources configured on your robot like resources that are already built-in to the Robot Development Kit (RDK).

Two key concepts exist across all Viam resources (both built-in and modular) to facilitate this: APIs and models.

Key concepts

APIs

Every Viam resource exposes an Application Programming Interface (API). This can be understood as a description of how you can interact with that resource. Each API is described through protocol buffers. Viam SDKs expose these APIs.

Each Viam resource’s API is uniquely namespaced as a colon-delimited-triplet in the form of namespace:type:subtype.

For example:

  • The API of built-in component camera is rdk:component:camera, which exposes methods such as GetImage().
  • The API of built-in service vision is rdk:service:vision, which exposes methods such as GetDetectionsFromCamera().

Models

A model describes a specific implementation of a resource that implements (speaks) its API. Models allow you to control different versions of resource types with a consistent interface.

For example:

Some DC motors use just GPIO, while other DC motors use serial protocols like SPI bus. Regardless, you can power any motor model that implements the rdk:component:motor API with the SetPower() method.

Models are also uniquely namespaced as colon-delimited-triplets in the form of namespace:family:name.

For example:

  • The rdk:builtin:gpio model of the rdk:component:motor API provides RDK support for GPIO-controlled DC motors.
  • The rdk:builtin:DMC4000 model of the same rdk:component:motor API provides RDK support for the DMC4000 motor.

A common use-case for modular resources is to create a new model using an existing Viam API. However, you can also create and expose new API types using modular resources.

Use a modular resource with your robot

Add a modular resource to your robot configuration in five steps:

  1. Code a module in Go or Python, using the module support libraries provided by the Python or Go Viam SDK.
  2. Compile or package the module code into an executable.
  3. Save the executable in a location your viam-server instance can access.
  4. Add a module referencing this executable to the configuration of your robot.
  5. Add a new component or service referencing the custom resource provided by the configured module to the configuration of your robot.

Code your module

Code a module in the Go or Python programming languages with Viam’s SDKs that does the following:

  1. Code a new resource model implementing all methods the Viam RDK requires in the API definition of its built-in type (ex. rdk:component:base).
  2. Code a main program to serve as the module itself, using the module helpers provided by your chosen SDK.
  3. Import the API and model(s) into the main program, and register them with the module helper SDK.
  4. Compile and/or package your program.
  1. Define the messages and methods of the new API in protobuf, then generate code in Python or Go and use the generated code to implement the higher level server and client functions required.
  2. Code at least one model of this new resource. Make sure to implement every method required in your API definition.
  3. Code a main program to serve as the module itself, using the module helpers provided by your chosen SDK.
  4. Import the API and model(s) into the main program, and register them with the module helper SDK.
  5. Compile and/or package your program.

For example:

Click to view example code from a module implementing a new model of the base component built-in resource

Make your module executable

To add a module to your robot, you need to have an executable file that runs your module when executed, can take a local socket as a command line argument, and cleanly exits when sent a termination signal. Your options for completing this step are flexible, as this file does not need to be in a raw binary format.

If using the Go SDK, Go will build a binary when you compile your module.

If using the Python SDK, one option is creating and save a new shell script (.sh) that runs your module. For example:

#!/bin/sh
cd <path-to-your-module-directory>

# Be sure to use `exec` so that termination signals reach the python process,
# or handle forwarding termination signals manually
exec python3 <your-module-directory-name>.<main-program-filename> $@

To make this shell script executable, run the following command in your terminal:

sudo chmod +x <FILEPATH>/<FILENAME>

Make sure viam-server can access your executable

Ensure that the code defining your module is saved where the instance of viam-server behind your robot can read and execute it.

For example, if you are running viam-server on an Raspberry Pi, you’ll need to save the module on the Pi’s filesystem.

Obtain the real (absolute) path to the executable file on your computer/board’s filesystem by running the following command in your terminal:

realpath <path-to-your-module-directory>/<your-module>

Configure your module

To configure your new module on your robot, navigate to the Config tab of your robot’s page on the Viam app and click on the Modules subtab.

The following properties are available for modules:

NameTypeInclusionDescription
namestringRequiredName of the module you are registering.
executable_pathstringRequiredThe robot’s computer’s filesystem path to the module executable.

Add these properties to your module’s configuration:

Creation of a new module in the Viam app config builder.

{
  "modules": [
    {
      "name": "<your-module-name>",
      "executable_path": "<path-on-your-filesystem-to/your-module-directory>/<your_executable.sh>"
    }
  ]
}

Configure your modular resource

Once you have configured a module as part of your robot configuration, you can instantiate any number of instances of a modular resource made available by that module by adding new components or services configured with your modular resources’ new type or model.

The following properties are available for modular resources:

NameTypeInclusionDescription
namespacestringRequiredThe namespace of the API (the first part of the API triplet).
typestringRequiredThe subtype of the API (the third part of the API triplet).
namestringRequiredWhat you want to name this instance of your modular resource.
modelstringRequiredThe full triplet of the modular resource.
depends_onarrayOptionalThe name of components you want to confirm are available on your robot alongside your modular resource. Usually a board.

All standard properties for configuration, such as attributes and depends_on, are also supported for modular resources. The attributes available vary depending on your implementation.

{
  "components": [
    {
      "namespace": "<your-module-namespace>",
      "type": "<your-resource-type>",
      "model": "<model-namespace>:<model-family-name>:<model-name>",
      "name": "<your-module-name>",
      "depends_on": [],
    }
  ],
  "modules": [ ... ] // < INSERT YOUR MODULE CONFIGURATION >
}

The following is an example configuration for a base modular resource implementation. The configuration adds acme:demo:mybase as a modular resource from the module my_base. The custom model is configured as a component with the name “my-custom-base-1” and can be interfaced with the Viam base API:

{
    "components": [
        {
            "type": "board",
            "name": "main-board",
            "model": "pi"
        },
        {
        "type": "base",
        "name": "my-custom-base-1",
        "model": "acme:demo:mybase",
        "namespace": "rdk",
        "attributes": {},
        "depends_on": [ "main-board" ]
        }
    ],
    "modules": [
    {
      "name": "my-custom-base",
      "executable_path": "/home/my_username/my_base/run.sh"
    }
  ]
}

More information

Modular resource management

Dependency Management

Modular resources may depend on other built-in resources or other modular resources, and vice versa. The Viam RDK handles dependency management.

Start-up

The RDK ensures that any configured modules are loaded automatically on start-up, and that configured modular resource instances are started alongside configured built-in resource instances.

Reconfiguration

When you change the configuration of a Viam robot, the behavior of modular resource instances versus built-in resource instances is equivalent. This means you can add, modify, and remove a modular resource instance from a running robot as normal.

Shutdown

During robot shutdown, the RDK handles modular resource instances similarly to built-in resource instances - it signals them for shutdown in topological (dependency) order.

Modular resources as remotes

Remote parts may load their own modules and provide modular resources, just as the main part can. This means that you can compose a robot of any number of parts running in different compute locations, each containing both built-in and custom resources.

Limitations

Custom models of the arm component type are not yet supported, as kinematic information is not currently exposed through the arm API.