Introduction
Internet of Things (IoT) has revolutionized how we interact with the world. It involves connecting everyday objects to the internet, enabling them to collect, send, and receive data. This connectivity allows for smarter decision-making, automation, and a new level of interactivity between the physical and digital worlds.
As a Cloud Architect, I’ve had the opportunity to work on several IoT cloud projects where the data from connected devices was processed centrally in AWS. These projects involved analyzing the data to drive critical business logic decisions, leading to improved efficiency, enhanced user experiences, and even the creation of new business models.
In this article, I’ll guide you through a step-by-step process of connecting an Arduino MKR1010 to AWS IoT Core. This tutorial is designed to help you understand how to leverage AWS’s powerful cloud services to manage and analyze IoT data, helping you to get started the way for your own innovative projects.
What is Arduino MKR Wifi 1010?
The Arduino MKR WiFi 1010 is the easiest point of entry to basic IoT and pico-network application design. Whether you are looking at building a sensor network connected to your office or home router, or if you want to create a Bluetooth® Low Energy device sending data to a cellphone, the MKR WiFi 1010 is your one-stop-solution for many of the basic IoT application scenarios.
The board is based on the SAMD21 microcontroller, which is a low-power ARM Cortex-M0+ processor, and includes an integrated u-blox NINA-W102 module for WiFi and Bluetooth connectivity.
Development Environment
Prerequisites
1- AWS IoT Account
- Sign up for an AWS account if you don’t already have one: AWS Sign-Up
- Select a predefined AWS region, ideally one closer to your current location, in our case will be Paris (eu-west-3)
Caution: not all regions support AWS IoT
2- Platform.io
I've selected Platform.io as development environment for Arduino projects because it offers enhanced features compared to the traditional Arduino IDE. It integrates seamlessly with Visual Studio Code, providing advanced code editing tools, debugging capabilities, and project management features
- I recommend to follow the Platform.io Quick start guide
3- Arduino MKR1010 and USB cable
Arduino MKR WiFi 1010 board.
Micro-USB to USB cable to connect the MKR1010 to your computer.
Configuring the Board
In order to securely connect the Board to AWS there is a mandatory step that we have to follow. We have to create an sketch to generate a CSR for a private key generated in an ECC508/ECC608
crypto chip slot. Once the skecth is uploaded to the board, the user is prompted for the following information that is contained in the generated CSR:
country
state or province
locality
organization
organizational unit
common name
We can consider optional all fields except common name
. After providing it, the user can also select a slot number to use for the private key.
Let's define the steps to generate the CSR:
Open Platform.io and ensure that the Board is available
Create new Project named
ArduinoCSRConfig
Install
ArduinoECCX08
library for this ProjectIn
/src
folder, modifymain.cpp
to include the sketch required, should be like:
/*
ArduinoECCX08 - CSR (Certificate Signing Request
The circuit:
- Arduino MKR board equipped with ECC508 or ECC608 chip
This example code is in the public domain.
*/
#include <Arduino.h>
#include <ArduinoECCX08.h>
#include <utility/ECCX08CSR.h>
#include <utility/ECCX08DefaultTLSConfig.h>
String readLine() {
String line;
while (1) {
if (Serial.available()) {
char c = Serial.read();
if (c == '\r') {
// ignore
continue;
} else if (c == '\n') {
break;
}
line += c;
}
}
return line;
}
String promptAndReadLine(const char* prompt, const char* defaultValue) {
Serial.print(prompt);
Serial.print(" [");
Serial.print(defaultValue);
Serial.print("]: ");
String s = readLine();
if (s.length() == 0) {
s = defaultValue;
}
Serial.println(s);
return s;
}
void setup() {
Serial.begin(9600);
while (!Serial);
if (!ECCX08.begin()) {
Serial.println("No ECCX08 present!");
while (1);
}
String serialNumber = ECCX08.serialNumber();
Serial.print("ECCX08 Serial Number = ");
Serial.println(serialNumber);
Serial.println();
if (!ECCX08.locked()) {
String lock = promptAndReadLine("The ECCX08 on your board is not locked, would you like to PERMANENTLY configure and lock it now? (y/N)", "N");
lock.toLowerCase();
if (!lock.startsWith("y")) {
Serial.println("Unfortunately you can't proceed without locking it :(");
while (1);
}
if (!ECCX08.writeConfiguration(ECCX08_DEFAULT_TLS_CONFIG)) {
Serial.println("Writing ECCX08 configuration failed!");
while (1);
}
if (!ECCX08.lock()) {
Serial.println("Locking ECCX08 configuration failed!");
while (1);
}
Serial.println("ECCX08 locked successfully");
Serial.println();
}
Serial.println("Hi there, in order to generate a new CSR for your board, we'll need the following information ...");
Serial.println();
String country = promptAndReadLine("Country Name (2 letter code)", "");
String stateOrProvince = promptAndReadLine("State or Province Name (full name)", "");
String locality = promptAndReadLine("Locality Name (eg, city)", "");
String organization = promptAndReadLine("Organization Name (eg, company)", "");
String organizationalUnit = promptAndReadLine("Organizational Unit Name (eg, section)", "");
String common = promptAndReadLine("Common Name (e.g. server FQDN or YOUR name)", serialNumber.c_str());
String slot = promptAndReadLine("What slot would you like to use? (0 - 4)", "0");
String generateNewKey = promptAndReadLine("Would you like to generate a new private key? (Y/n)", "Y");
Serial.println();
generateNewKey.toLowerCase();
if (!ECCX08CSR.begin(slot.toInt(), generateNewKey.startsWith("y"))) {
Serial.println("Error starting CSR generation!");
while (1);
}
ECCX08CSR.setCountryName(country);
ECCX08CSR.setStateProvinceName(stateOrProvince);
ECCX08CSR.setLocalityName(locality);
ECCX08CSR.setOrganizationName(organization);
ECCX08CSR.setOrganizationalUnitName(organizationalUnit);
ECCX08CSR.setCommonName(common);
String csr = ECCX08CSR.end();
if (!csr) {
Serial.println("Error generating CSR!");
while (1);
}
Serial.println("Here's your CSR, enjoy!");
Serial.println();
Serial.println(csr);
}
void loop() {
// do nothing
}
- Click
Upload and monitor
and, in order to generate a new CSR, define only thecommon name
with the valueArduinoMKR1010
- Copy the generated CSR text including
"-----BEGIN CERTIFICATE REQUEST-----"
and"-----END CERTIFICATE REQUEST-----"
and save it into a.csr
file.
Finally we have a CSR to identify the board and be able to communicate with AWs IoT core securely.
NOTE: This locking process is permanent and cannot be undone, but it is necessary to use the crypto element. The configuration set by the sketch allows the use of 5 private key slots with any Cloud provider (or server). A CSR can be regenerated at any time for each of the other four slots.
Connecting to AWS IoT Core
Once the Board is properly initialized and the CSR configured, we can proceed with the tutorial and connect the device to IoT core using MQTT protocol. AWS requires to use X.509 certificates for authentication. Let's follow the steps:
1- Create Arduino Board as AWS IoT Thing
Access to your AWS account in the predefined region. For the purpose of our example, it is Paris(
eu-west-3
)To create the Board in AWS IoT as a Thing we will use the same name that we used for the common name in the CSR configuration:
ArduinoMKR1010
By the way, we are going to skip the CSR certificate Upload for the Board
2- Create a project in Platform.io environment
Create e new project in Platform.io, for our example we have named it
Hello World - MKR wifi 101
-
Install the required libraries:
- WiFiNINA
- ArduinoECCX08
- ArduinoBearSSL
- ArduinoMqttClient
- As we have two projects in our VsCode workspace, I recommend to set the new created project as a default:
3- Create the Certificate and attach it to the Thing
Move the
.csr
file generated in previous steps to a more appropiate folder inside this project, for example into a/resources
folder.Create the certificate in AWS IoT by updating the csr generated.
Download the generated
.pem.crt
certificate file generated in AWS and save it in the/resources
folder. We will need this file in the Board in order to communicate with cloud.Ensure that the certificate is activated and Attached to the Thing created:
4- Create a Policy
For the scope of this example, we are going ot create an open policy, that will allow all iot actions on AWS.
For real life projects, I strongly recommend to create a strict policy and follow the least principle privilege.
Once the policy is created, it should b attached to the Certificate created in the previous step.
5- Implement the Connecting sketch to AWS IoT Core
- In the project
Hello World - MKR wifi 101
, modify the filesrc/main.cpp
and add the following code:
/*
AWS IoT WiFi
This sketch securely connects to an AWS IoT using MQTT over WiFi.
It uses a private key stored in the ATECC508A and a public
certificate for SSL/TLS authetication.
It publishes a message every second to arduino/outgoing
topic and subscribes to messages on the arduino/incoming
topic.
The circuit:
- Arduino MKR WiFi 1010 or MKR1000
*/
#include <Arduino.h>
#include <ArduinoBearSSL.h>
#include <ArduinoECCX08.h>
#include <ArduinoMqttClient.h>
#include <WiFiNINA.h> // change to #include <WiFi101.h> for MKR1000
#include "arduino_secrets.h"
/////// Enter your sensitive data in arduino_secrets.h
const char ssid[] = SECRET_SSID;
const char pass[] = SECRET_PASS;
const char broker[] = SECRET_BROKER;
const char* certificate = SECRET_CERTIFICATE;
WiFiClient wifiClient; // Used for the TCP socket connection
BearSSLClient sslClient(wifiClient); // Used for SSL/TLS connection, integrates with ECC508
MqttClient mqttClient(sslClient);
unsigned long lastMillis = 0;
unsigned long getTime() {
// get the current time from the WiFi module
return WiFi.getTime();
}
void connectWiFi() {
Serial.print("Attempting to connect to the SSID: ");
Serial.print(ssid);
Serial.print(" ");
bool connected = false;
int retryCount = 0;
const int maxRetries = 10;
while (!connected && retryCount< maxRetries) {
if (WiFi.begin(ssid, pass) == WL_CONNECTED) {
connected = true;
} else {
// failed, retry
Serial.print(".");
delay(1000);
retryCount++;
}
}
if (connected) {
Serial.println();
Serial.println("You're connected to the network");
Serial.println();
} else {
Serial.println();
Serial.println("Failed to connect to the network after multiple attempts.");
}
}
void connectMQTT() {
Serial.print("Attempting to MQTT broker: ");
Serial.print(broker);
Serial.println(" ");
while (!mqttClient.connect(broker, 8883)) {
// failed, retry
Serial.print(".");
delay(3000);
}
Serial.println();
Serial.println("You're connected to the MQTT broker");
Serial.println();
// subscribe to a topic
mqttClient.subscribe("arduino/incoming");
}
void publishMessage() {
Serial.println("Publishing message");
// send message, the Print interface can be used to set the message contents
mqttClient.beginMessage("arduino/outgoing");
mqttClient.print("{\"deviceModel\": \"MKR 1010\" ");
mqttClient.print(",\"temperature\": " );
mqttClient.print(random(15, 30));
mqttClient.print(",\"humidity\": ");
mqttClient.print(random(1, 500));
mqttClient.print(",\"vehicleId\": \"MKR1010-1");
mqttClient.print("\"}");
mqttClient.endMessage();
}
void onMessageReceived(int messageSize) {
// we received a message, print out the topic and contents
Serial.print("Received a message with topic '");
Serial.print(mqttClient.messageTopic());
Serial.print("', length ");
Serial.print(messageSize);
Serial.println(" bytes:");
// use the Stream interface to print the contents
while (mqttClient.available()) {
Serial.print((char)mqttClient.read());
}
Serial.println();
Serial.println();
}
void setup() {
Serial.begin(115200);
while (!Serial);
if (!ECCX08.begin()) {
Serial.println("No ECCX08 present!");
while (1);
}
// Set a callback to get the current time
// used to validate the servers certificate
ArduinoBearSSL.onGetTime(getTime);
// Set the ECCX08 slot to use for the private key
// and the accompanying public certificate for it
sslClient.setEccSlot(0, certificate);
// Optional, set the client id used for MQTT,
// each device that is connected to the broker
// must have a unique client id. The MQTTClient will generate
// a client id for you based on the millis() value if not set
//
// mqttClient.setId("clientId");
// Set the message callback, this function is
// called when the MQTTClient receives a message
mqttClient.onMessage(onMessageReceived);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
int numNetworks = WiFi.scanNetworks();
Serial.println("Discovered " + String(numNetworks) + " Networks");
connectWiFi();
}
if (!mqttClient.connected()) {
// MQTT client is disconnected, connect
connectMQTT();
}
// poll for new MQTT messages and send keep alives
mqttClient.poll();
// publish a message roughly every 1 seconds.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
publishMessage();
}
}
Create in
/include
folder a file for the secrets namedarduino_secrets.h
that has already been included in the main sketch file.arduino_secrets.h
will contain the secrets and variables required to connect, first to the Wifi and the to the Cloud provider, for example:
#define SECRET_SSID "MIWIFI_SSID"
#define SECRET_PASS "XXXXXXXXXXXX"
#define SECRET_BROKER "a21nf7ui6d7k9-ats.iot.eu-central-1.amazonaws.com"
const char SECRET_CERTIFICATE[] = R"(
-----BEGIN CERTIFICATE-----
MIIClTCCAX2gAwIBAgIVAMtpt+I79Uj24rlM4MVB4Q2mqffdMA0GCSqGSIb3DQEB
CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t
IEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0yNDA5MDIxNDA3
MDJaFw00OTEyMzEyMzU5NTlaMCQxCjAIBgNVBAYTASAxFjAUBgNVBAMTDU15TUtS
V2lGaTEwMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARo/XaUyfCffmCSIyl1
x1Tc4pbi4d1gu+IzOa98Fl52HaxxroFkDrKCiC6cyaK653BFXkRpWcQ7ivmNouO7
Fz06o2AwXjAfBgNVHSMEGDAWgBTwrTWex0zOILY7T5K6neYroFcHYDAdBgNVHQ4E
FgQUiqwBn2n3ixwwL4E8YI5d4b4eWdMwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8E
BAMCB4AwDQYJKoZIhvcNAQELBQADggEBAK8iH5HpmGaxw9XQh/zyPsKxGG/dXXWr
U/mbZH6HGnf/Ux3ULnyZElBOB99KgqCutu8+k8lgpR8gLtsfC3XpL+TwIG4UHVak
fqP//CFl+n0DRWZvqw2wyvKBVyP1d9WUcTAW7x1k5ZN3AeXDT1Iy/KIkxhQXHIQi
wA1nw+YPDafHNFAviLKrUYd+Sx9hpKLDLI17609HbeY/9dsZh8MvxUmgjod7jvIy
0EGOgf8EhqyDvTVMb18ZUT0LKCwDWpMug0fZ+XK0kHA98Th0ndkE3997MqJSuY2u
tPND16KqhQPWim/akbJb1yDmP0M5xzF9Vtoh9Tv8JLxynmgc0KDq5/g=
-----END CERTIFICATE-----
)";
Replace the secrets for the correct ones, specially the
SECRET_CERTIFICATE
, that should be the one that is contained in the certificate file that was downloaded from AWS in a previous step inresources
folder.To replace the
SECRET_BROKER
, the endpoint should be obtained from AWS:
6- Connecting to AWS IoT
- Once the project code is properly added to
Hello World - MKR wifi 101
, Ensure that the Board is connected through USB and that the device is available in PIO HOME Devices:
There are several Project Tasks availables. We need to Build the sketch code and upload it to the Board connected.
-
I recommend to use the following tasks:
Full Clean
Build
Upload and Monitor
Finally, if all steps have worked successfully, a Serial monitor terminal is openned and the Board is going to Connect to the Wifi and send messages to AWS IoT
Testing and Monitoring Data
The board is continously sending a MQTT message to IoT core with random generated data every second with this composition, see example:
{
"deviceModel": "MKR 1010",
"temperature": 22,
"humidity": 317,
"vehicleId": "MKR1010-1"
}
1- ** Monitor Outgoing Data from the Board**
- We have to use the MQTT test client to Subscribe to the topic
arduino/outgoing
2- Send a Message to the Board
We should subscribe to the topic
arduino/incoming
and publis a message from the MQTT Test client in AWS IoTThe message sent should appear in the Serial console that we have openned in Platform.io
Conclusion
In this guide, we’ve walked through the process of connecting the Arduino MKR WiFi 1010 to AWS IoT Core using Platform.io framework in Visual Studio Code. You’ve learned how to set up your development environment, configure AWS IoT Core, and establish communication between your Arduino board and the cloud using the MQTT protocol.
Although this is a specific project, it can be considered the first step toward building scalable IoT solutions. In my opinion, Arduino is one of the best platforms for growing in the IoT space and for enjoying the process of building personal projects. It’s also a viable option for production systems due to its flexibility and ecosystem. Additionally, using Platform.io enhances local development capabilities and simplifies the integration of new tasks, making it an excellent tool for both hobbyists and professionals.
As a next step, I plan to extend this project by creating an AWS IoT Rule that processes the data coming from the Arduino MKR1010 and integrates it with a real-time platform. This will involve uploading the data from the incoming MQTT topic directly to DynamoDB.
I’ll be covering this in my upcoming articles, where I’ll explain the integration in more detail. You can also check out my previous article on building a real-time platform using AWS services here, which will serve as a foundation for the next phase of this project.
Reference
1- Github Project:
https://github.com/acriado-dev/arduino-mkr-1010-aws-iot-core
2- Arduino MKR 1010 Official Docs:
3- Platform.io
https://docs.platformio.org/en/latest/what-is-platformio.html
4- AWS IoT core Developer guide
https://docs.aws.amazon.com/iot/latest/developerguide/what-is-aws-iot.html