AWS Robotics Blog

Manage Robot Deployment from External Applications with AWS

This blog references AWS RoboMaker Fleet Management, a feature that has been deprecated. You should now use AWS IoT Greengrass V2 for deploying and managing your robot software. To learn more about this process, see our blog on how to Deploy and manage ROS robots with AWS IoT Greengrass 2.0 and Docker.

Introduction

An increasingly large number of companion robots, vacuum cleaning robots, delivery robots, and inspection drones are shipped to consumers or deployed to the field every day. Today, any robotics developer or company can use AWS RoboMaker’s fleet management features to deploy applications and application updates to their robots and manage robot fleets of any scale. One request we have heard from customers is to have more control over when an application deployment should be executed on a specific robot. This request is common in the consumer robotics space, where a robot typically comes with a companion mobile application and this application requires the end user’s explicit approval for deployment. To address this request, we are launching Conditional OTA in AWS RoboMaker – this feature allows you to specify multiple conditions (ex. battery level is higher than 50%, Robot is NOT moving etc.) before our service will attempt to deploy an application update to a robot. If these conditions are not satisfied, the deployment will be postponed and subsequently retried until it either succeeds or the attempts exceeds the timeout configured by the user. This new conditional OTA feature will ensure that your robot is in a desirable state before the AWS RoboMaker service will perform application download, installation, and reboot of the robot. Should the conditions specified by the user for an update fail, the previously deployed application will continue to operate on the robot.

In this post, I illustrate how to use the Conditional OTA of AWS RoboMaker with AWS IoT to manage your deployment from any of the external applications (mobile app, laptop, cloud-based application, etc.) to your robot. We demonstrate how to deploy a ROS application to a robot with conditional script in AWS RoboMaker. The conditional script publishes a message into a topic through the IoT message broker and then listens for the response from your application to trigger the application deployment.

How it works

AWS RoboMaker Fleet Management uses AWS IoT Greengrass to deploy an application to a robot in a fleet. After the user creates a deployment job, identifying the application that is to be deployed, RoboMaker Fleet Management will begin to deploy that application to all robots in the fleet. Once a deployment job has started, the conditional script will be run to verify the status of the robot. The sample script in this article publishes a deployment message to the IoT Topic. Since the user’s application subscribes to the same topic in the IoT message broker, they receive a notification identifying the robot that is receiving the deployment. In the sample external application process, the user can decide whether to continue the robot deployment. If the user approves the deployment, the current ROS processes running in the robot will be terminated and AWS IoT Greengrass will download the application and then start the new application. If application cannot be deployed before the configured timeout period, the deployment will fail with the reason (failure code) being DownloadConditionFailure.

Steps-by-step guide

Creating an external application to manage your robot deployment in AWS RoboMaker involves the following steps:

  1. Create a robot in AWS RoboMaker.
  2. Create a fleet and register robot to the fleet in AWS RoboMaker.
  3. Create a robot application in AWS RoboMaker.
  4. Create an IoT thing in AWS IoT.
  5. Create a conditional file and upload to your S3.
  6. Start your external application process.
  7. Create a deployment job with conditional OTA.
  8. Monitor the robot deployment status based on your application input.

Prerequisites

You need a robot with Greengrass installed on it – you will see the installation instructions in the documentation. If you don’t have a robot available, you can set up an EC2 instance.

Step 1: Create a robot

First, you need to create a robot in the AWS RoboMaker service. You need to have access to be able to SSH to the Robot.

  1. In the AWS RoboMaker console, choose Robot, Create Robot.
  2. Fill in the name and select the architecture for your robot.
  3. Select Create New GreengrassGroup and fill in the Greengrass prefix.
  4. Create a new IAM Role.
  5. Download the GreengrassCore, Certificates and follow the instructions in the console to install them onto your robot.

Step 2: Create a fleet and register robot

Next, create a robot fleet and register your robot in the fleet.

  1. In the AWS RoboMaker console, choose Fleet, Create Fleet.
  2. Fill in the name and create the fleet.
  3. Choose Register New and select the robot you just created.

Step 3: Create a robot application

Create a Robot application in AWS RoboMaker. You can find the source code in the robot_ws directory of the aws-robotics GitHub repo. To use your own robot application, replace the Amazon S3 link with the location of your application bundle. For more information, see Creating a Robot Application and Sample Robot Application.

  1. Upload your Robot Application into your Amazon S3.
  2. In the AWS RoboMaker console, choose Robot applications, Create robot application.
  3. Fill in the name and select a right ROS version based on Robot Application.
  4. Under Sources, for your robot architecture source file, enter the s3 location.

Step 4: Create an IoT thing in AWS IoT

Create a thing in AWS IoT. Go to IoT Core -> Manage -> Thing -> Create

  • Download the IoT certificates
    • This is required to be installed in both – the external application as well as the robot.
  • Attach an IoT policy with the thing, which allows the relative topics resource operation.
    • For this post, you need the policy to allow the following operations: Publish, Subscribe and Receive on topics deployment/condition and deployment/condition/confirmed. And Connect on <client_id> which is used in external process and conditional script.
    • Sample Policy:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Subscribe",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:<region>:<account-id>:topic/deployment/condition",
        "arn:aws:iot:<region>:<account-id>:topic/deployment/condition/confirmed"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "arn:aws:iot:<region>:<account-id>:client/<client_id>"
      ]
    }
  ]
 }

Step 5: Create a conditional file and upload to your S3

Create your conditional file – this will be executed on your Robot before starting the deployment.

This sample Python script uses AWS IoT to:

  • Send a deployment message to the topic deployment/condition – this notifies the external app.
  • Listen on the topic deployment/condition/confirmed – this receives acceptance from the external app.
#!/usr/bin/env python

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import time
import os
import sys
import threading
import json

host = "<iot_host_endpoint>"
rootCAPath = "<root-CA_path>"
privateKeyPath = "<private_key_path>"
certificatePath = "<certificate_key_path>"
clientId = "<client_id>"
deployment_topic = "deployment/condition"
deployment_confirmed_topic = "deployment/condition/confirmed"
robot = os.environ['AWS_ROBOMAKER_DEPLOYMENT_ROBOT_ARN']
port = 8883

threadLock = threading.Lock()
done = False
conditionPass = False

# Custom MQTT message callback
def customCallback(client, userdata, message):
    content = message.payload
    with threadLock:
        global done, conditionPass
        done = True
        if content == "True":
            conditionPass = True
        else:
            conditionPass = False

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.INFO)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)
myAWSIoTMQTTClient.configureEndpoint(host, port)
myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec

# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
myAWSIoTMQTTClient.subscribe(deployment_confirmed_topic, 1, customCallback)

loopCount = 0
while True:
    # waiting confirmation message from external application...
    time.sleep(5)
    with threadLock:
        if done:
            if conditionPass:
                print("User approved - condition passed.\n")
                exit(0)
            else:
                sys.exit('User rejected - condition failed.\n')
        else:
            message = {}
            message['message'] = robot
            message['sequence'] = loopCount
            messageJson = json.dumps(message)
            myAWSIoTMQTTClient.publish(deployment_topic, messageJson, 1)
            loopCount += 1

Step 6: Start your external application process

Start an external application, which waits for the message from the conditional script. You can run this as a mobile app or just a simple process on your laptop. This Python script uses AWS IoT to:

  • Listen on the topic deployment/condition – the response from the conditional script.
  • Publish the message to deployment/condition/confirmed – once you confirm.
#!/usr/bin/env python

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import time
import threading

host = "<iot_host_endpoint>"
rootCAPath = "<root-CA_path>"
privateKeyPath = "<private_key_path>"
certificatePath = "<certificate_key_path>"
clientId = "<client_id>"
deployment_topic = "deployment/condition"
deployment_confirmed_topic = "deployment/condition/confirmed"
port = 8883

threadLock = threading.Lock()
done = False
deploymentStarted = False

# Custom MQTT message callback
def customCallback(client, userdata, message):
    global done, deploymentStarted
    with threadLock:
        deploymentStarted = True

    print("Received a robot deployment message: ")
    print(message.payload)
    start_deployment = yes_or_no("Do you want to continue the robot deployment?")
    with threadLock:
        done = True
    if start_deployment:
        myAWSIoTMQTTClient.publish(deployment_confirmed_topic, "True", 1)
    else:
        myAWSIoTMQTTClient.publish(deployment_confirmed_topic, "False", 1)


def yes_or_no(question):
    while "the answer is invalid":
        reply = str(raw_input(question+' (y/n): ')).lower().strip()
        if reply[0] == 'y':
            return True
        if reply[0] == 'n':
            return False

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.WARN)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)
myAWSIoTMQTTClient.configureEndpoint(host, port)
myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)
# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec

# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
myAWSIoTMQTTClient.subscribe(deployment_topic, 1, customCallback)
time.sleep(2)

while True:
    time.sleep(5)
    with threadLock:
        if done:
            exit(0)
        else:
            if not deploymentStarted:
                # wait the conditional file send the message
                print("Waiting A Robot Deployment... \n")

Step 7: Create a deployment job with conditional OTA

Create a deployment job for the fleet.

  1. In the AWS RoboMaker console, choose Deployments, Create Deployment.
  2. Select the fleet, RobotApplication that you created above.
  3. Fill package name and launch file for the RobotApplication.
  4. In Deployment settings, fill in the S3 location for the conditional script you just upload to S3.
  5. Also select 10 minutes for the deployment timeout.
  6. Click Create.

Step 8: Monitor the robot deployment status based on your application input

The latest step is to monitor and manage the robot deployment. Once the deployment has started, you should receive a message from the robot – it should be displayed in your external process like this:

Input y to approve the robot deployment, after which you can check robot deployment status in the AWS RoboMaker console.

If you say n, the deployment will be retried from AWS RoboMaker side. After the configured timeout (10 minutes in this case), the deployment will fail with the reason being DownloadConditionFailure.

Conclusion

This post demonstrated how to use Conditional OTA with AWS IoT to manage Robot deployment from your external application. To learn more about AWS RoboMaker, please visit https://thinkwithwp.com/robomaker/.

We hope you find this post useful. We always welcome your feedback and comments on new ways that you discover to make the most of AWS features.