Connecting an Arduino MKR WiFi 1010 to AWS IoT Core

amatore

Amador Criado

Posted on September 13, 2024

Connecting an Arduino MKR WiFi 1010 to AWS IoT Core

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

Image description

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

VSCode Extensions Manager and PlatformIO IDE auto-installer

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 Project

  • In /src folder, modify main.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
}

Enter fullscreen mode Exit fullscreen mode
  • Click Upload and monitor and, in order to generate a new CSR, define only the common name with the value ArduinoMKR1010

  • 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 file src/main.cppand 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();
      }
    }
Enter fullscreen mode Exit fullscreen mode
  • Create in /include folder a file for the secrets named arduino_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-----
)";
Enter fullscreen mode Exit fullscreen mode
  • 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 in resources 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"
}
Enter fullscreen mode Exit fullscreen mode

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 IoT

  • The 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:

https://store.arduino.cc/en-es/products/arduino-mkr-wifi-1010?gad_source=1&gclid=CjwKCAjwreW2BhBhEiwAavLwfFKUmllSUvjuY6mNsFugNXUCh-aoR3kiqFvGyX0dToSSzvty08dyBhoCE28QAvD_BwE

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

💖 💪 🙅 🚩
amatore
Amador Criado

Posted on September 13, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related