This tutorial refers to a product that has reached its end-of-life status.

Securely Connecting a MKR GSM 1400 to Google Cloud IoT Core

In this tutorial, you'll learn how to connect your Arduino MKR GSM 1400 board securely to Google Cloud Platform (GCP) IoT Core.

Components and Supplies

Apps and Online Services

About This Project

Introduction

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 Cloud IoT platform, provides a complete solution for collecting, processing, analyzing, and visualizing IoT data in real time to support improved operational efficiency.

Devices can connect to GCP IoT Core using HTTP or MQTT. This tutorial will walk you through how to connect an Arduino MKR GSM 1400 board securely to GCP IoT Core using an MQTT client. MQTT (Message Queuing Telemetry Transport) is a M2M (machine-to-machine) connectivity protocol which provides a messaging subscription and publish transport.

Devices must use JSON Web Tokens (JWTs) for authentication, more information on JWTs can be found in RFC 7519. GCP IoT Core supports both RSA and Elliptic Curve algorithms to verify JSON Web Signatures (JWS). More information on JWS can be found in RFC 7515.

Every Arduino MKR board with on-board connectivity, including the MKR GSM 1400, is equipped with a Microchip ATECC508A or ATECC608A crypto element. This crypto element can be used to securely generate and store a 256-bit ECC (Elliptic Curve Cryptography) key. We'll be using a private key stored inside the crypto element to sign the JWT.

Software and Hardware Setup

If you don't have the Arduino IDE installed on your computer, download and install it.

Once it is installed, make sure you have the latest "Arduino SAMD Boards" package installed. You can check by opening the Arduino IDE, and opening the Tools -> Board: "..." -> Board Manager... menu entry, and searching for "Arduino SAMD". At the time of writing 1.6.20 was the latest version.

The Boards Manager.
The Boards Manager.

Next you'll need to install the Arduino libraries that will be used, using the Arduino IDE's library manager. Open Sketch -> Include Library -> Manage Libraries.. menu, search for and individually install each of the following libraries:

  • MKRGSM
  • Arduino_JSON
  • ArduinoECCX08 (version 1.3.0 or later)
  • ArduinoMqttClient (version 0.1.3 or later)
  • Arduino Cloud Provider Examples (version 1.2.0 or later)

Now insert the micro SIM card in the slot on the bottom of the MKR GSM 1400 board, connect the antenna, and attach the 3.7V Lipo battery to the JST PH connector. Then plug in the MKR GSM 1400 with the micro USB cable to your computer, select the serial port in the Arduino IDE using the Tools -> Port "..." menu and also select Arduino MKR GSM 1400 in the Tools -> Board "..." menu.

Select MKR GSM 1400.
Select MKR GSM 1400.

Configuring and Adding the Board to GCP IoT Core

As mentioned above, GCP IoT Core requires devices that connect using the MQTT protocol to use JWT for authentication. We'll use a sketch to generate a private and public key on the board, then add the PEM value of the public key to the GCP IoT Core console.

The private and public can be generated using an example sketch from the ArduinoECCX08 library. Open the sketch in the Arduino IDE using the File -> Examples -> ArduinoECCX08 -> Tools -> ECCX08JWSPublicKey. Click the "Upload" button to build and upload the sketch to your board, then open the Serial Monitor. Make sure the line ending configuration is set to "Both NL & CR."

This sketch will prompt you to permanently configure your ATECC508A to ECC608A crypto element if it is not configured and locked.

NOTE: This locking process is permanent and irreversible, but is needed to use the the crypto element - the configuration the sketch sets allows you to use 5 private key slots with any Cloud provider(or server) and a private key can be regenerated any time for any of the 5 private key slots (0 - 4).

When the board is shipped from the factory, the crypto element is in an unconfigured and unlocked state.

After this, you will be prompted for what slot to use. For this tutorial we'll be using slot 0 to generate and store the private key used for a public key (slots 1 to 4 can be used to generate and store additional private keys if needed). Note: Since the private key is generated inside the crypto element it never leaves the device and is stored securely and cannot be read.

Public key shown in the Serial Monitor.
Public key shown in the Serial Monitor.

Copy the generated public key value, in this screenshot the value is:

1-----BEGIN PUBLIC KEY-----
2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFl4+DXufU84AhDGib7aMwmYwUVAp9coRdC9jOdzR
3e2kqGWFEb+QP4V4YUK9Zy7PsmRABi1sWgxiAoEhg1FEQgg==
4-----END PUBLIC KEY-----

We will use it in a later step when adding the device to GCP IoT Core.

Now that we have a PEM public key, we need to login into the GCP IoT Core console and create a new device for it.

Open a web browser and go to https://cloud.google.com/ and click the "Sign In" link to login with your Google ID.

Sign in to Google Cloud.
Sign in to Google Cloud.
Once you are logged in, click the "GO TO CONSOLE" button. Then you will see the main dashboard.
Once you are logged in, click the "GO TO CONSOLE" button. Then you will see the main dashboard.
Click the "CREATE" link to create a new project.
Click the "CREATE" link to create a new project.
You will be prompted for a project name, we'll be using "MKR GCP Tutorial" for the name. Click the "CREATE" button to continue.
You will be prompted for a project name, we'll be using "MKR GCP Tutorial" for the name. Click the "CREATE" button to continue.
After the project has been create you will be presented a dashboard view of it.
After the project has been create you will be presented a dashboard view of it.
Now click the menu icon in the top left hand side and click on "IoT Core".
Now click the menu icon in the top left hand side and click on "IoT Core".
You will be prompted to enable the API, click the "Enable API" button.
You will be prompted to enable the API, click the "Enable API" button.

Once the API is enabled, you will be prompted to create a device registry.

Click the "Create a device registry" button to proceed.
Click the "Create a device registry" button to proceed.

You will then be presented with a form. Fill in the "Registry ID", select a region. In the screenshot below "MKR_GCP_Tutorial" was entered for the registry ID and "us-central1" was selected as the region. After the form has been filled in, click the "Create" button.

Complete the form.
Complete the form.
You will be then presented with details of the registry.
You will be then presented with details of the registry.
To add a new device, click "Devices" link on the navigation bar on the left hand side.
To add a new device, click "Devices" link on the navigation bar on the left hand side.
Then click "+ CREATE A DEVICE" in the heading at the top of the page.
Then click "+ CREATE A DEVICE" in the heading at the top of the page.

Enter the device name, in the screenshot below "MyMKRGSM1400" was used. "ES256" must be selected as the "Public key format". Paste the PEM public key generated on the board earlier into the "Public key value" text area. Then click the "Create" button.

Enter your device name.
Enter your device name.

Connecting the Board to GCP IoT Core

1) Open the GCP IoT Core GSM sketch in the Arduino IDE using File -> Examples ->Arduino Cloud Provider Examples -> GoogleCloudPlatformIoTCore->GCP_IoT_Core_GSM.

2) In the arduino_secrets.h tab, fill in the pin (if required) for the SIM card, as well as the GPRS APN, username and password for the cellular carrier you are using.

1// GSM settings
2#define SECRET_PINNUMBER ""
3#define SECRET_GPRS_APN "GPRS_APN" // replace your GPRS APN
4#define SECRET_GPRS_LOGIN "login" // replace with your GPRS login
5#define SECRET_GPRS_PASSWORD "password" // replace with your GPRS password

4) Then update the project id, Cloud region, registry id and device id values.

1// Fill in your Google Cloud Platform - IoT Core info
2#define SECRET_PROJECT_ID ""
3#define SECRET_CLOUD_REGION ""
4#define SECRET_REGISTRY_ID ""
5#define SECRET_DEVICE_ID ""

The project id value can be found by clicking the menu bar at the top of the GCP console. For the steps above the values are:

1#define SECRET_PROJECT_ID "mkr-gcp-tutorial"
2#define SECRET_CLOUD_REGION "us-central1"
3#define SECRET_REGISTRY_ID "MKR_GCP_Tutorial"
4#define SECRET_DEVICE_ID "MyMKRGSM1400"

5) Upload the sketch below to your board and open the serial monitor. The board will attempt to connect to the cellular network and if successful try to connect to GCP IoT Core using MQTT.

Complete Sketch

1/*
2 GCP (Google Cloud Platform) IoT Core GSM
3 This sketch securely connects to GCP IoT Core using MQTT over GSM/3G.
4 It uses a private key stored in the ATECC508A and a JSON Web Token (JWT) with
5 a JSON Web Signature (JWS).
6 It publishes a message every 5 seconds to "/devices/{deviceId}/state" topic
7 and subscribes to messages on the "/devices/{deviceId}/config" and
8 "/devices/{deviceId}/commands/#" topics.
9 The circuit:
10 - MKR GSM 1400 board
11 - Antenna
12 - SIM card with a data plan
13 - LiPo battery
14 This example code is in the public domain.
15*/
16
17#include <ArduinoECCX08.h>
18#include <utility/ECCX08JWS.h>
19#include <ArduinoMqttClient.h>
20#include <Arduino_JSON.h>
21#include <MKRGSM.h>
22
23#include "arduino_secrets.h"
24
25/////// Enter your sensitive data in arduino_secrets.h
26const char pinnumber[] = SECRET_PINNUMBER;
27const char gprs_apn[] = SECRET_GPRS_APN;
28const char gprs_login[] = SECRET_GPRS_LOGIN;
29const char gprs_password[] = SECRET_GPRS_PASSWORD;
30
31const char projectId[] = SECRET_PROJECT_ID;
32const char cloudRegion[] = SECRET_CLOUD_REGION;
33const char registryId[] = SECRET_REGISTRY_ID;
34const String deviceId = SECRET_DEVICE_ID;
35
36const char broker[] = "mqtt.googleapis.com";
37
38GSM gsmAccess;
39GPRS gprs;
40
41GSMSSLClient gsmSslClient;
42MqttClient mqttClient(gsmSslClient);
43
44unsigned long lastMillis = 0;
45
46void setup() {
47 Serial.begin(9600);
48 while (!Serial);
49
50 if (!ECCX08.begin()) {
51 Serial.println("No ECCX08 present!");
52 while (1);
53 }
54
55 // Calculate and set the client id used for MQTT
56 String clientId = calculateClientId();
57
58 mqttClient.setId(clientId);
59
60 // Set the message callback, this function is
61 // called when the MQTTClient receives a message
62 mqttClient.onMessage(onMessageReceived);
63}
64
65void loop() {
66 if (gsmAccess.status() != GSM_READY || gprs.status() != GPRS_READY) {
67 connectGSM();
68 }
69
70 if (!mqttClient.connected()) {
71 // MQTT client is disconnected, connect
72 connectMQTT();
73 }
74
75 // poll for new MQTT messages and send keep alive
76 mqttClient.poll();
77
78 // publish a message roughly every 5 seconds.
79 if (millis() - lastMillis > 5000) {
80 lastMillis = millis();
81
82 publishMessage();
83 }
84}
85
86unsigned long getTime() {
87 // get the current time from the cellular module
88 return gsmAccess.getTime();
89}
90
91void connectGSM() {
92 Serial.println("Attempting to connect to the cellular network");
93
94 while ((gsmAccess.begin(pinnumber) != GSM_READY) ||
95 (gprs.attachGPRS(gprs_apn, gprs_login, gprs_password) != GPRS_READY)) {
96 // failed, retry
97 Serial.print(".");
98 delay(1000);
99 }
100
101 Serial.println("You're connected to the cellular network");
102 Serial.println();
103}
104
105void connectMQTT() {
106 Serial.print("Attempting to connect to MQTT broker: ");
107 Serial.print(broker);
108 Serial.println(" ");
109
110 while (!mqttClient.connected()) {
111 // Calculate the JWT and assign it as the password
112 String jwt = calculateJWT();
113
114 mqttClient.setUsernamePassword("", jwt);
115
116 if (!mqttClient.connect(broker, 8883)) {
117 // failed, retry
118 Serial.print(".");
119 delay(5000);
120 }
121 }
122 Serial.println();
123
124 Serial.println("You're connected to the MQTT broker");
125 Serial.println();
126
127 // subscribe to topics
128 mqttClient.subscribe("/devices/" + deviceId + "/config", 1);
129 mqttClient.subscribe("/devices/" + deviceId + "/commands/#");
130}
131
132String calculateClientId() {
133 String clientId;
134
135 // Format:
136 //
137 // projects/{project-id}/locations/{cloud-region}/registries/{registry-id}/devices/{device-id}
138 //
139
140 clientId += "projects/";
141 clientId += projectId;
142 clientId += "/locations/";
143 clientId += cloudRegion;
144 clientId += "/registries/";
145 clientId += registryId;
146 clientId += "/devices/";
147 clientId += deviceId;
148
149 return clientId;
150}
151
152String calculateJWT() {
153 unsigned long now = getTime();
154
155 // calculate the JWT, based on:
156 // https://cloud.google.com/iot/docs/how-tos/credentials/jwts
157 JSONVar jwtHeader;
158 JSONVar jwtClaim;
159
160 jwtHeader["alg"] = "ES256";
161 jwtHeader["typ"] = "JWT";
162
163 jwtClaim["aud"] = projectId;
164 jwtClaim["iat"] = now;
165 jwtClaim["exp"] = now + (24L * 60L * 60L); // expires in 24 hours
166
167 return ECCX08JWS.sign(0, JSON.stringify(jwtHeader), JSON.stringify(jwtClaim));
168}
169
170void publishMessage() {
171 Serial.println("Publishing message");
172
173 // send message, the Print interface can be used to set the message contents
174 mqttClient.beginMessage("/devices/" + deviceId + "/state");
175 mqttClient.print("hello ");
176 mqttClient.print(millis());
177 mqttClient.endMessage();
178}
179
180void onMessageReceived(int messageSize) {
181 // we received a message, print out the topic and contents
182 Serial.print("Received a message with topic '");
183 Serial.print(mqttClient.messageTopic());
184 Serial.print("', length ");
185 Serial.print(messageSize);
186 Serial.println(" bytes:");
187
188 // use the Stream interface to print the contents
189 while (mqttClient.available()) {
190 Serial.print((char)mqttClient.read());
191 }
192 Serial.println();
193
194 Serial.println();
195}
Serial Monitor45
Serial Monitor45

Interacting with the Board on GCP IoT Core

Now that your board has successfully connected to GCP IoT Core, we can use the GCP IoT Core console to interact with it. The sketch sends a message to the /devices/{deviceId}/state topic every 5 seconds and listens for messages on both /devices/{deviceId}/config topic and /devices/{deviceId}/commands/# topics.

In the device page in GCP IoT Core console, click the "SEND COMMAND" button.
In the device page in GCP IoT Core console, click the "SEND COMMAND" button.

A modal dialog will appear, where you can enter a message to send. In the screenshot below "Hello There!" was entered. Click the "SEND COMMAND" button to send the message.

Click send command.
Click send command.

Once the board receives the message it will print it on the Serial Monitor.

The message shown on the Serial Monitor.
The message shown on the Serial Monitor.

To view the messages the board is sending, click "Configuration & state history" tab.

Click "Configuration & state history" tab.
Click "Configuration & state history" tab.

The messages will appear in Base64 encoded format, to view the value click and entry in the list and select the "Text" radio button.

Click the "Text" radio button.
Click the "Text" radio button.

In the screenshot above, the board was sending a "hello 464488" value, the 464488 value is the result of the millis() function on the board.

Conclusion

In this tutorial, we covered how to securely use an Arduino MKR GSM 1400 board with GCP IoT Core. A signed JWT was used to authenticate with GCP IoT Core using the MQTT protocol with the ATECC508A or ATECC608A storing the private key used to sign the JWT. MQTT messages were sent to and from the board.

This is just the beginning, you can use GCP IoT Core with many of the other services GCP provides!

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.