Previous
Support hardware
This guide shows you how to write a module with control logic for a machine:
DoCommand methodFor microcontrollers, see Micro-RDK modules and Over-the-air firmware updates instead.
You must have one machine running viam-server.
If your control logic depends on any hardware or software resources to function, you must configure those hardware and software resources.
Install the Viam CLI. You will use the CLI to generate the template you will use to write your control logic:
If you wish to test your control logic locally, follow these instructions on the computer on which you are running viam-server.
Install the CLI.
You must have the Viam CLI installed to generate and upload modules:
To download the Viam CLI on a macOS computer, install brew and run the following commands:
brew tap viamrobotics/brews
brew install viam
To download the Viam CLI on a Linux computer with the aarch64 architecture, run the following commands:
sudo curl -o /usr/local/bin/viam https://storage.googleapis.com/packages.viam.com/apps/viam-cli/viam-cli-stable-linux-arm64
sudo chmod a+rx /usr/local/bin/viam
To download the Viam CLI on a Linux computer with the amd64 (Intel x86_64) architecture, run the following commands:
sudo curl -o /usr/local/bin/viam https://storage.googleapis.com/packages.viam.com/apps/viam-cli/viam-cli-stable-linux-amd64
sudo chmod a+rx /usr/local/bin/viam
You can also install the Viam CLI using brew on Linux amd64 (Intel x86_64):
brew tap viamrobotics/brews
brew install viam
Download the binary and run it directly to use the Viam CLI on a Windows computer.
If you have Go installed, you can build the Viam CLI directly from source using the go install command:
go install go.viam.com/rdk/cli/viam@latest
To confirm viam is installed and ready to use, issue the viam command from your terminal.
If you see help instructions, everything is correctly installed.
If you do not see help instructions, add your local PATH variable.
If you use bash as your shell, you can use the following command:
echo 'export PATH="$HOME/go/bin:$PATH"' >> ~/.bashrc
For more information see install the Viam CLI.
Run the module generate command in your terminal:
viam module generate --resource-subtype=generic-component
Follow the prompts, selecting the following options:
Module name: Your choice, for example control-logic
Language: Your choice
Visibility: Private
Namespace/Organization ID: Navigate to your organization settings through the menu in upper right corner of the page.
Find the Public namespace and copy that string.
In the example snippets below, the namespace is naomi.
Resource to be added to the module: Generic Component.
You can use any resource type. The choice of resource type affects the API methods that you must implement. You can choose any component API or service API.
If you plan to use the control logic mostly on one component or service, choose the same component or service and implement the control logic in the available API methods for that resource.
If no resource API fits, use the Generic type and implement the logic in the DoCommand method.
All resource APIs contain the generic DoCommand method to implement any functionality that does not fit into other API methods.
DoCommand is often used to implement control logic, as you can pass commands as arbitrary JSON objects, such as {“action”: “start”}.
For simplicity, this guide uses the generic component which only supports the DoCommand method.
Model name: Your choice, for example control-logic
Enable cloud build: Choose Yes if you are using GitHub or want to use cloud build.
Register module: Yes
Press the Enter key and the generator will create a folder for your control logic component.
Open the python file in the
The following example shows how you might implement logic that toggles an LED on and off.
Any resources that you wish to access from your control logic need to be identified and instantiated.
To keep your code loosely coupled, we recommend passing the resource names in the configuration attributes of the control logic.
We must modify the validate_config method to ensure all required values are passed in correctly and then instantiate the resource in the reconfigure method.
Pass resources in configuration.
The validate_config method serves two purposes:
validate_config method is called whenever the module is started or a configuration change occurs.viam-server waits until all returned dependencies are available before starting this component.
@classmethod
def validate_config(
cls, config: ComponentConfig
) -> Tuple[Sequence[str], Sequence[str]]:
fields = config.attributes.fields
if "board_name" not in fields:
raise Exception("missing required board_name attribute")
elif not fields["board_name"].HasField("string_value"):
raise Exception("board_name must be a string")
board_name = fields["board_name"].string_value
if not board_name:
raise ValueError("board_name cannot be empty")
if "pin" not in fields:
raise Exception("missing required pin attribute")
elif not fields["pin"].HasField("string_value"):
raise Exception("pin must be a string")
pin = fields["pin"].string_value
if not pin:
raise ValueError("pin cannot be empty")
# Return the board as a required dependency (just the name, not the full ResourceName)
req_deps = [board_name]
return req_deps, []
Set up instance parameters
When your new model gets added to your machine, its new() method gets called.
You can use it to store any instance variables.
viam-server passes the required dependencies when the control logic resource is reconfiguring.
From these dependencies you can get the board and store it in an instance variable.
@classmethod
def new(
cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]
) -> Self:
toggler = super().new(config, dependencies)
toggler.board_name = config.attributes.fields["board_name"].string_value
board_resource_name = Board.get_resource_name(toggler.board_name)
board_resource = dependencies[board_resource_name]
toggler.board = cast(Board, board_resource)
toggler.pin = config.attributes.fields["pin"].string_value
return toggler
Add the following imports at the top of
from typing import cast
from viam.components.board import Board
The new method gets called whenever the control logic module starts or when a configuration change occurs for the resource itself.
If this is a problem, consider writing state to a file on disk and adding logic to handle subsequent calls to the reconfigure method gracefully.
Write the control logic.
Update your logic in the do_command method to use the board:
async def do_command(
self,
command: Mapping[str, ValueTypes],
*,
timeout: Optional[float] = None,
**kwargs
) -> Mapping[str, ValueTypes]:
result = {key: False for key in command.keys()}
for name, args in command.items():
if name == "action" and args == "toggle":
pin = await self.board.gpio_pin_by_name(name=self.pin)
high = await pin.get()
if high:
await pin.set(high=False)
else:
await pin.set(high=True)
result[name] = True
return result
For a complete tutorial, see Tutorial: Desk Safari. For more examples, check the Viam registry
For more information on accessing components and services, see Module dependencies.
You can test your module locally before uploading it to the registry.
To get your module onto your machine, hot reloading builds and packages it and then uses the shell service to copy it to the machine for testing. If your files are already on the machine, you can add the module manually instead.
Run the following command to build the module and add it to your machine:
viam module reload-local --cloud-config /path/to/viam.json
viam module reload --part-id 123abc45-1234-432c-aabc-z1y111x23a00
For more information, see the viam module documentation.
You may need to refresh your machine page for your module to show up.
Navigate to your machine’s CONFIGURE page.
Click the + button, select Local module, then again select Local module.
Enter the path to the automatically-generated viam-server uses this path to start the module.
Example module:
For the control-logic module, the path should resemble /home/yourname/control-logic/run.sh on Linux, or /Users/yourname/control-logic/run.sh on macOS.
Save the config.
Configure your resource as a local component or service.
Click +, click Local module, then click Local component or Local service depending on your resource type and fill in the fields as follows:
<namespace>:control-logic:control-logic, you can see the full triplet in the module’s <resource-type>resource-1If you use other machine resources, add their configuration values in the resource’s configuration field and updating the names as needed:
{
"board_name": "board-1",
"pin": "13"
}
Save the config.
Use the TEST panel to test the resource.
If you are encountering errors, check the LOGS tab for more information.
Iterate.
If you make changes to your module code, you must restart your module for the changes to take effect. Click on the … menu near the module and select Restart.
You can use the DoCommand method from the web UI or from the Viam SDKs:
On the CONTROL or the CONFIGURE tab, use the DoCommand panel:
Copy and paste the following command input:
{
"action": "toggle"
}
Click Execute to call DoCommand() with the command input on your machine.

You can run your control logic with the DoCommand() method from the Python SDK:
await control_logic.do_command({"action": "toggle"})
These steps manually test the control logic, to run the logic automatically, see Run control logic automatically with jobs.
To run control logic, use a job which calls the DoCommand method periodically.
job-1, and click Create.5 seconds.DoCommand Method and specify the Command { "action": "toggle" }.For testing purposes, you can also send this command manually.
Once you have thoroughly tested your module, continue to package and deploy it.
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!