Gateways are a powerful feature of Cloud IoT Core that bridges communication between constrained devices and Cloud IoT Core. For example, devices that have computational limitations (limited memory or cannot create JWTs) or protocol limitations (can only communicate via Bluetooth or LoRa) can still leverage the capabilities of Cloud IoT Core and the rest of Google Cloud for processing, analyzing, and visualizing data in real time.

What you'll build

In this codelab, you'll build a system that consists of a single gateway and a collection of constrained devices that can only communicate via UDP sockets. These will all be virtual devices, but will provide the groundwork for adding the hardware components later on.

What you'll learn

What you'll need

Open Cloud Shell

First, make sure the GOOGLE_CLOUD_PROJECT environment variable contains your project ID, which should look something like this.

$ echo $GOOGLE_CLOUD_PROJECT
qwiklabs-gcp-44776a13dea667a6

If this isn't populated properly, the environment variable ${DEVSHELL_PROJECT_ID:-Cloud Shell} is populated with your project ID. Set the environment variable by running the following each time you open a new Cloud shell tab:

export GOOGLE_CLOUD_PROJECT=${DEVSHELL_PROJECT_ID:-Cloud Shell}

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

gcloud pubsub topics create gateway-topic --project=${GOOGLE_CLOUD_PROJECT}

gcloud pubsub subscriptions create gateways-sub --topic=gateway-topic --project=${GOOGLE_CLOUD_PROJECT}

Create your device registry using the following gcloud command:

gcloud iot registries create gateway-registry \
  --region=us-central1 --event-notification-config=topic=gateway-topic \
  --project=${GOOGLE_CLOUD_PROJECT}

Next, you will need to generate an RSA256 private key and corresponding X509 public certificate that will be used for authenticating your gateway device.

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

Now, you will create the actual gateway, which is a specific type of device that can handle communication for other devices. Create your gateway using the public certificate you created.

gcloud iot devices create test-gateway \
  --region=us-central1 \
  --registry=gateway-registry \
  --auth-method=association-only \
  --device-type=gateway \
  --public-key path=rsa_cert.pem,type=rsa-x509-pem

Create three devices, named led-light, thermostat, and light-sensor, that will be connected to the gateway. Note, you don't have to specify a public key here since Cloud IoT Core will authenticate the devices based only on their association with the gateway.

gcloud iot devices create led-light \
  --region=us-central1 \
  --registry=gateway-registry \
  --device-type=non-gateway \
  --project=${GOOGLE_CLOUD_PROJECT}

gcloud iot devices create thermostat \
  --region=us-central1 \
  --registry=gateway-registry \
  --device-type=non-gateway \
  --project=${GOOGLE_CLOUD_PROJECT}

gcloud iot devices create light-sensor \
  --region=us-central1 \
  --registry=gateway-registry \
  --device-type=non-gateway \
  --project=${GOOGLE_CLOUD_PROJECT}

Lastly, you'll need to bind each device to the gateway you just created.

gcloud iot devices gateways bind \
  --device=led-light \
  --device-region=us-central1 \
  --device-registry=gateway-registry\
  --gateway=test-gateway \
  --gateway-region=us-central1 \
  --gateway-registry=gateway-registry \
  --project=${GOOGLE_CLOUD_PROJECT}

gcloud iot devices gateways bind \
  --device=thermostat \
  --device-region=us-central1 \
  --device-registry=gateway-registry\
  --gateway=test-gateway \
  --gateway-region=us-central1 \
  --gateway-registry=gateway-registry \
  --project=${GOOGLE_CLOUD_PROJECT}

gcloud iot devices gateways bind \
  --device=light-sensor \
  --device-region=us-central1 \
  --device-registry=gateway-registry\
  --gateway=test-gateway \
  --gateway-region=us-central1 \
  --gateway-registry=gateway-registry \
  --project=${GOOGLE_CLOUD_PROJECT}

Binding creates an association between the devices and the gateway that Cloud IoT Core checks to authenticate the devices. Recall the argument --auth-method=association-only from the command that created the gateway. This means that Cloud IoT Core will authenticate devices based on the bound association between a gateway and a device, which removes the burden of storing JWTs on devices. You can read more about other authentication methods here.

To make sure everything is set up properly, open the Cloud IoT Core console.

You should see a single registry from the Registries page. If not, ensure the proper project ID is selected at the top of the page.

Click on the name of the registry, and select Gateways on the left. You should see the single gateway that you just created.

Next, click the gateway ID to get more details about that gateway. Then, click the Bound devices tab to see the devices you associated with this gateway.

If your configurations match the screenshots, congratulations! You have now:

In the Cloud Shell, clone the following repository by running the following:

git clone https://github.com/googlecloudplatform/python-docs-samples
cd python-docs-samples/iot/api-client/codelabs

Next, initialize the virtual environment in python3.

sudo apt install python3-venv
python3 -m venv env

Activate the virtual environment and install the dependencies.

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

Next, retrieve the root certificate from Google.

wget https://pki.goog/roots.pem

Finally, start the gateway:

python ./gateway.py \
--registry_id=gateway-registry \
--gateway_id=test-gateway \
--cloud_region=us-central1 \
--private_key_file=$HOME/rsa_private.pem \
--algorithm=RS256 \
--ca_certs=roots.pem \
--mqtt_bridge_hostname=mqtt.googleapis.com \
--mqtt_bridge_port=8883 \
--jwt_expires_minutes=1200

If your terminal looks like the above screenshot, your gateway server is now successfully listening for socket connections from devices. The code for the gateway is slightly complex, but essentially does the following:

  1. Connects to Cloud IoT Core's MQTT bridge at mqtt.googleapis.com:8883 using the provided RSA private key: rsa_private.pem.
  2. Creates a UDP server socket, and waits for clients to connect.
  3. Depending on the message from the device, performs different actions such as attaching/detaching devices, relaying telemetry events to Cloud IoT Core, or relaying configuration changes from Cloud IoT Core to devices.

Keep the gateway process running in your current tab. In your Cloud Shell, open three additional sessions by pressing the Add Cloud Shell session button.

As you continue through the following steps, do not terminate the device processes as you move on to the next steps. Leave each device's process running in a separate tab/session. At the end, you will see the gateway simultaneously handling connections from all three devices to Cloud IoT Core.

The first device you will simulate is a simple LED light that can turn on and off based on configuration changes from Cloud IoT Core.

Switch to the first new Cloud Shell session that you created in the previous step, and

start the LED light by running the following commands:

cd python-docs-samples/iot/api-client/codelabs && source env/bin/activate
python ./ledlight.py led-light

Initially, the state of the LED should be off.

Now, navigate back to the Cloud IoT Core console in your browser. Select Devices on the left and then click on the led-light device. Try sending a configuration update to the device, with text "on". The gateway is listening for configuration updates and will relay the message to the virtual LED light.

The second device is a simulated thermostat that reports temperature and humidity to Cloud IoT Core.

Switch to the second new Cloud Shell session you created and start this device by running the following commands:

cd python-docs-samples/iot/api-client/codelabs && source env/bin/activate
python ./thermostat.py thermostat

This time, you won't need to use the console since all the device is doing is relaying simulated temperature/humidity to your gateway. You should notice that some temperature and humidity is being reported every second. Check the gateway tab to see the messages come in from the thermostat.

Since the device is authenticated by its bound association with the gateway, the gateway uses its signed JWTs to relay the telemetry data to Cloud IoT Core.

Return to the Cloud Shell and run the following command to see the telemetry events pulled from the topic via your subscription. Run the following in the last tab you created.

gcloud pubsub subscriptions pull gateways-sub --auto-ack --limit=20

You should see a portion of the telemetry events, including the data, message ID, and its attributes from Cloud IoT Core. Although out of scope for this lab, you can stream this information into BigQuery using Cloud Dataflow, or have incoming messages from Cloud PubSub trigger Cloud Functions.

The final device will be a simulated light sensor that you can configure remotely, including toggling the device on and off, as well as changing how frequently it publishes sensor data. This device will simultaneously receive and send messages to gateway via the UDP socket.

In the same tab that you pulled the data from your PubSub subscription, start the device by running the following command:

cd python-docs-samples/iot/api-client/codelabs && source env/bin/activate
python ./lightsensor.py light-sensor

Initially, The device will publish light data (in lux) at a rate of one reading per second.

Now, navigate back to the Cloud IoT Core console in your browser. Select Devices on the left and then click on the light-sensor device.

Try sending a configuration update to the device with the following JSON string:

{ "power": "off" }

The gateway will relay this information and the device should stop publishing sensor data until you enable the power again.

Next, try sending the following configuration:

{ "power": "on", "interval": 5 }

This will turn the device back on, so that it publishes telemetry events again. However, the same command also changes the interval at which the readings are being published, to around one reading per five seconds.

At this point, your PubSub topic should have received a lot of telemetry events, so rerun the subscription command and increase the limit parameter. Stop one of the device processes in any tab and run the following command.

gcloud pubsub subscriptions pull my-subscription --auto-ack --limit=100

In practice, services that ingest data from Pub/Sub should process that data in regular intervals as telemetry events are published.

Congratulations! You have just used gateways to manage devices that aren't directly connected to Cloud IoT Core. Since these are only simulated devices, the data that you obtained isn't very useful. Nonetheless, understanding the concepts used in this lab will provide working knowledge of managing constrained devices and how to get their data into the cloud for processing and analyzing.

Now you might have realized that turning on the LED light has no actual effect on the light sensor reading. Currently the light sensor is being changed by a random amount every few seconds. As a challenge, try opening a second socket on a different port and actually have the LED and light sensor communicate with each other.

Alternatively, if you would like to see how to connect hardware components to Cloud ioT Core, check out this community article that demonstrates the concepts from this lab with a Raspberry Pi.

The troubleshooting section of the Cloud IoT Core end-to-end sample has a few more helpful pointers if you get stuck.