What is OpenThread and Google Cloud IoT Core?

OpenThread is...

...an open-source implementation of the Thread networking protocol. Nest has released OpenThread to make the technology used in Nest products more broadly available to developers to accelerate the development of products for the connected home.

...OS and platform agnostic, with a narrow platform abstraction layer and a small memory footprint, making it highly portable. It supports both system-on-chip (SoC) and network co-processor (NCP) designs.

...a Thread Certified Component, implementing all features defined in the Thread 1.1.1 specification, including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as Border Router support.

More information about Thread can be found at threadgroup.org. Thread is a registered trademark of the Thread Group, Inc.

OpenThread

Cloud IoT Core is a fully managed service that allows you to easily and securely connect, manage, and ingest data from millions of globally dispersed devices. Cloud IoT Core, in combination with other services on Google Cloud platform, provides a complete solution for collecting, processing, analyzing, and visualizing IoT data in real time to support improved operational efficiency.

This chart shows how the Google Cloud IoT Core bridge connects devices to Google Cloud Services. It also highlights the data and analytics tools for warehousing and inspecting your data.

What you will build

You will build a prototype of an IoT system with Google Cloud IoT Core. You will connect a simulated OpenThread device to internet via border router. The device will publish temperature data to their telemetry feeds, and a server consumes the telemetry data from a Cloud Pub/Sub topic. The server then decides whether to turn on or off the individual devices' fans, via a Cloud IoT Core configuration update.

What you'll learn

What you'll need

Install docker

This Codelab is designed to use Docker on a Linux machine.

Download and Install Docker for your linux distribution.

Download Docker

Download border router docker image

Once Docker is installed, open a terminal window and pull the openthread/codelab_otsim Docker image. This image features OpenThread and wpantund pre-built and ready to use for this Codelab.

docker pull openthread/codelab_cloudiot:latest

Note that it may take a few minutes to completely download.

Click the following link to open the end-to-end sample in Google Cloud Shell:

Open the sample in Google Cloud

If you have already cloned the repo, input 1 to cd into the sample folder and then cd iot/api-client/end_to_end_example; otherwise, enter the following command to `cd` into the sample folder:

cd iot/api-client/end_to_end_example

Next, initialize virtual environment and install the sample dependencies:

virtualenv env && source env/bin/activate
pip install -r requirements.txt

At this point, you can check that you have installed the Python dependencies correctly by running all the Python script without passing any parameters:

python cloudiot_pubsub_example_server.py

If the dependencies installed successfully, the programs will print their respective usage messages, for example:

usage: cloudiot_pubsub_example_server.py [-h] --project_id PROJECT_ID
                                         --pubsub_subscription
                                         PUBSUB_SUBSCRIPTION
                                         [--service_account_json SERVICE_ACCOUNT_JSON]

If you see an error similar to `ImportError: No module named ...`, go back and make sure you installed Virtual Environment correctly, or see the Python Development Environment Setup Guide for detailed information on using Python with Google Cloud. Now that you have the program libraries installed, it's time to set up your Google Cloud IoT Core project.

To ensure your Cloud shell has the latest versions of the Cloud IoT Core API installed, update the gcloud components.

gcloud components update

Open the Google Cloud Console and enable the API if it has not already been enabled on your Kiosk or Google account.

Enable Cloud IoT Core

Enable Cloud Pub / Sub

After you have enabled the API, select the current project to get your project ID.

The selection menu to the right of the Google Cloud Logo is highlighted showing the menu item to click for project selection.

The project name shown in the menu bar can differ from the project ID, so make sure you set it to the correct value.

gcloud config set project <your-project-id>

Create a Pub/Sub topic using the following gcloud command:

gcloud pubsub topics create tour-pub --project="${DEVSHELL_PROJECT_ID:-Cloud Shell}"
gcloud pubsub subscriptions create tour-sub --topic=tour-pub

Create your Cloud IoT Device Registry using the following gcloud command:

gcloud iot registries create tour-registry \
  --region=us-central1 --event-notification-config=topic=tour-pub

Next, you will need to generate RSA public and private keys that will be used for authenticating your virtual device when it connects.

openssl req -x509 -newkey rsa:2048 -days 3650 -keyout rsa_private.pem \
    -nodes -out rsa_public.pem -subj "/CN=unused"

Make a copy of rsa private key onto your local host machine.

With your keys in hand, you're ready to register a device. Register your device using the public key.

gcloud iot devices create test-dev --region=us-central1 \
  --registry=tour-registry \
  --public-key path=rsa_public.pem,type=rs256

Congratulations, you have now set up Cloud Pub/Sub, created your device registry, and added a device to the registry!

Now that you have created all of the Cloud resources you need to connect your device and communicate with it via Pub/Sub, it's time to simulate a device and server.

Edit cloudiot_pubsub_example_server.py and replace the code used to generate service account credentials from a provided JSON file to instead use the built-in credentials for Compute Engine, which is what is running under the hood of the Cloud Shell.

Replace the following code:

    def __init__(self, service_account_json):
        credentials = ServiceAccountCredentials.from_json_keyfile_name(
            service_account_json, API_SCOPES)

With:

    def __init__(self, service_account_json):        
        from google.auth import compute_engine
        credentials = compute_engine.Credentials()

Now that you have changed the server to use the Compute Engine credentials, start the server using the following syntax:

python cloudiot_pubsub_example_server.py \
    --project_id="PROJECT_ID" \
    --pubsub_subscription=PUBSUB_SUBSCRIPTION

For example, if you used the example values in previous commands, the command is:

python cloudiot_pubsub_example_server.py \
    --project_id="${DEVSHELL_PROJECT_ID:-Cloud Shell}" \
    --pubsub_subscription=tour-sub

When the server starts, you will see the message "Listening for messages on projects/your-project-id/subscriptions/tour," which indicates the server is running.

Start the codelab Docker container

docker run --sysctl \
"net.ipv6.conf.all.disable_ipv6=0 \
net.ipv4.conf.all.forwarding=1 \
net.ipv6.conf.all.forwarding=1" \
-it --privileged codelab_cloudiot

The entrypoint script will launch the OpenThread border router.

Form the Thread network

OTBR docker provides a web gui to manage Thread networks. We directly use the web api to form the Thread network. Inside docker container, run:

curl --header "Content-Type: application/json" --request POST --data \
'{"networkKey":"00112233445566778899aabbccddeeff","prefix":"fd11:22::","defaultRoute":true,"extPanId":"dead00beef00cafe","panId":"0x1234","passphrase":"123456","channel":11,"networkName":"OpenThread"}' \
http://127.0.0.1/form_network

Build ot-rtos with Cloud IoT Core config

ot-rtos is a library integrating OpenThread, lwIP and FreeRTOS to make task management and network development easy with OpenThread. We are using the linux simulation command line application provided in the repository so you can easily move to a real device with FreeRTOS support.

First copy the private key into docker container. On local host machine, run:

docker cp rsa_private.pem <codelab-container-id>:/app/ 

You can find docker container id by running:

docker ps

Inside docker run:

cd /app/ot-rtos
mkdir build && cd build
cmake .. -DPLATFORM_NAME=linux -DCLOUDIOT_DEVICE=test-dev \
-DCLOUDIOT_REGISTRY=tour-registry -DCLOUDIOT_PROJECT=<your project id> \
-DCLOUDIOT_REGION=us-central1 -DCLOUDIOT_PRIV_KEY=/app/rsa_private.pem 
make -j8

Run the virtual device and observe

Now run the OpenThread command line application

cd ~/ot-rtos/build
./ot_cli_linux 2

If you don't see the > prompt after running this command, press enter.

Now connect the device to thread network:

> panid 0x1234
Done
> ifconfig up
Done
> thread start
Done

Wait for a few seconds for the device to connect itself to thread network. Then you can start publishing device status to Google Cloud IoT Core:

> test mqtt

The following section shows the output of the device that is transmitting its telemetry events to the server.

The device (test-dev) has a temperature of: -4
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: -3
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: -2
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: -1
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: 0
The device (test-dev) has a temperature of: 1
The device (test-dev) has a temperature of: 2
The device (test-dev) has a temperature of: 3
The device (test-dev) has a temperature of: 4
The device (test-dev) has a temperature of: -4
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: -3
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: -2
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: -1
Setting fan state for device test-dev to off.
The device (test-dev) has a temperature of: 0
The device (test-dev) has a temperature of: 1
The device (test-dev) has a temperature of: 2
The device (test-dev) has a temperature of: 3
The device (test-dev) has a temperature of: 4
The device (test-dev) has a temperature of: 5
The device (test-dev) has a temperature of: 6
The device (test-dev) has a temperature of: 7
The device (test-dev) has a temperature of: 8
The device (test-dev) has a temperature of: 9
The device (test-dev) has a temperature of: 10
The device (test-dev) has a temperature of: 11
Setting fan state for device test-dev to on.
The device (test-dev) has a temperature of: 12
Setting fan state for device test-dev to on.
The device (test-dev) has a temperature of: 13
Setting fan state for device test-dev to on.

The following section shows the output of the server that is subscribed to the telemetry events from the device.

Mqtt Connected
Connect done
data callback len=17
Topic /devices/test-dev/config get message len = 17 {"fan_on": false}
data callback len=17
Topic /devices/test-dev/config get message len = 17 {"fan_on": false}
Publish message: {"temperature": -4}
Publish message: {"temperature": -3}
data callback len=17
Topic /devices/test-dev/config get message len = 17 {"fan_on": false}
Publish message: {"temperature": -2}
data callback len=17
Topic /devices/test-dev/config get message len = 17 {"fan_on": false}
Publish message: {"temperature": -1}
data callback len=17
Topic /devices/test-dev/config get message len = 17 {"fan_on": false}
Publish message: {"temperature": 0}
Publish message: {"temperature": 1}
Publish message: {"temperature": 2}
Publish message: {"temperature": 3}
Publish message: {"temperature": 4}
Publish message: {"temperature": 5}
Publish message: {"temperature": 6}
Publish message: {"temperature": 7}
Publish message: {"temperature": 8}
Publish message: {"temperature": 9}
Publish message: {"temperature": 10}
Publish message: {"temperature": 11}
data callback len=16
Topic /devices/test-dev/config get message len = 16 {"fan_on": true}
Publish message: {"temperature": 12}
data callback len=16
Topic /devices/test-dev/config get message len = 16 {"fan_on": true}
Publish message: {"temperature": 13}
data callback len=16
Topic /devices/test-dev/config get message len = 16 {"fan_on": true}

Cleaning up

After you're finished with the codelab, run the following commands to clean up the resources you created on your Cloud account:

gcloud iot devices delete test-dev --region=us-central1 --registry=tour-registry
gcloud iot registries delete tour-registry --region=us-central1
gcloud pubsub subscriptions delete tour-sub
gcloud pubsub topics delete tour-pub