Conecte o ESP32 ao AWS IoT (com código Arduino)

Tempo de leitura: 8 minutes

Ultimamente, tenho construído alguns dispositivos IoT e sempre usei o microcontrolador ESP32. É um microcontrolador poderoso e tem Wi-Fi integrado, o que significa que posso conectá-lo ao AWS IoT para enviar dados do sensor para a nuvem para processamento e proteção.

Esta postagem mostrará como conectar seu ESP32 com AWS IoT. Liberte o poder da nuvem!

Observação: este projeto não usará o AWS IoT SDK, Mongoose ou FreeRTOS. Apenas uma combinação de bibliotecas do Arduino para juntar tudo.

 

 

Comecemos pelo princípio: WiFi

Antes de nos conectarmos ao AWS, o ESP32 precisa de uma conexão ativa com a Internet. Então, vamos começar escrevendo um código para se conectar a uma rede WiFi. Sinta-se à vontade para pular esta etapa se você já fez isso.

Normalmente defino as credenciais de WiFi na parte superior do arquivo para que sejam fáceis de alterar, caso seja necessário:

#include "WiFi.h"

// Wifi credentials
const char *WIFI_SSID = "YOUR WIFI NETWORK NAME";
const char *WIFI_PASSWORD = "WIFI PASSWORD";

Então, podemos escrever uma função simples que tenta se conectar a uma determinada rede:

void connectToWiFi()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  // Tente apenas 15 vezes para se conectar ao WiFi
  int retries = 0;
  while (WiFi.status() != WL_CONNECTED && retries < 15){
    delay(500);
    Serial.print(".");
    retries++;
  }

  // Se ainda não foi possível conectar ao Wi-Fi, adormeça por um minuto e tente novamente.
  if(WiFi.status() != WL_CONNECTED){
    esp_sleep_enable_timer_wakeup(1 * 60L * 1000000L);
    esp_deep_sleep_start();
  }
}

Esta função específica tentará conectar 15 vezes e, se falhar, fará com que o ESP32 entre no modo de hibernação por um minuto. Isso evita que o microcontrolador fique preso ao tentar se conectar ao WiFi (e potencialmente descarregando a bateria). Mas sinta-se a personalizá-lo como achar melhor.

 

Provisionando um certificado

A seguir: registrar seu dispositivo com AWS e criar um certificado para ele. O serviço IoT da Amazon é seguro por padrão e requer que cada dispositivo tenha um certificado exclusivo. Este certificado não apenas criptografa os dados que o dispositivo envia, mas também fornece uma maneira de bloquear dispositivos.

Comece fazendo login no console AWS e navegando até o console IoT Core. Aqui, vá em “Gerenciar> Coisas” e clique no botão “Criar”.

Nesse caso, vou criar apenas um único dispositivo, então clique em “Create a single thing”:

Dê um nome ao seu dispositivo (escreva-o, você precisará dele mais tarde) e, opcionalmente, dê um tipo e anexe-o a um grupo:

Em seguida, temos que gerar um certificado para nosso dispositivo para que ele possa se comunicar com a AWS de uma maneira segura. Clique em “Criação de certificado com um clique” (ou seja criativo e gere o seu no CSR):

A Amazon agora irá gerar certificados para o seu dispositivo. Baixe cada um (embora a chave pública não seja usada) e também baixe o “CA raiz para AWS IoT”. Além disso, não se esqueça de clicar no botão “Ativar”. Caso contrário, você acabará com um dispositivo que não pode se conectar porque seu certificado não está ativo.

A seguir: anexar uma política ao dispositivo. Esta política definirá o que um dispositivo tem permissão para fazer. Por padrão, os dispositivos não podem fazer nada. Esta é a política que uso para quase todos os meus dispositivos:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Subscribe"
      ],
      "Resource": [
        "arn:aws:iot:eu-west-1:ACCOUNT_ID:topicfilter/${iot:Connection.Thing.ThingName}",
        "arn:aws:iot:eu-west-1:ACCOUNT_ID:topicfilter/${iot:Connection.Thing.ThingName}/*",
        "arn:aws:iot:eu-west-1:ACCOUNT_ID:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/update"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "*"
      ],
      "Condition": {
        "Bool": {
          "iot:Connection.Thing.IsAttached": [
            "true"
          ]
        }
      }
    }
  ]
}

A política permite que os dispositivos publiquem em tópicos MQTT que começam com seus nomes e permite que eles atualizem seu documento sombra. Ele também verifica se o dispositivo está se conectando a um certificado anexado a ele. Caso contrário, a conexão é recusada.

Assim que a política for adicionada, clique em “Register Thing” para finalizar o processo de inscrição:

Tudo bem, a etapa final é anotar a URL de nosso terminal MQTT. No console AWS IoT, clique em “Settings” no canto inferior esquerdo. Você verá seu ponto de extremidade exclusivo à direita.

Nota lateral: este endpoint é exclusivo para sua conta AWS e é compartilhado entre todos os seus dispositivos. Se você deseja conectar vários dispositivos ao AWS IoT, você só precisa realizar esta etapa uma vez.

 

Codificando os certificados

Agora que temos os certificados para nosso dispositivo, podemos adicioná-los ao esboço do Arduino. Gostaria de armazená-los em um arquivo separado, então criei um arquivo certs.h indo ao menu “Sketch” e clicando em “Add File…”. Esta etapa é totalmente opcional.

A seguir, definiremos três variáveis para armazenar o CA raiz da Amazon AWS_CERT_CA, a chave privada do nosso dispositivo AWS_CERT_PRIVATE e o certificado do nosso dispositivo AWS_CERT_CRT. A chave pública que a Amazon gerou para nosso dispositivo não é necessária.

#ifndef certs_h
#define certs_h

// CA raiz da Amazon. Isso deve ser igual para todos.
const char AWS_CERT_CA[] = "-----BEGIN CERTIFICATE-----\n" \
"MIIDQTChkiG9w0CAimfz5m/jAo5gAwIBBgkqBAkPmljZbyjQsAgITBmy4vB4iANF\n" \
"ADA5MGQW1hem5sGQW1hemDVVUzEMQxBBDVhMQsYDVQQQGEwJQDExBBbWF6\n" \
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" \
"-----END CERTIFICATE-----\n";

// A chave privada do seu dispositivo
const char AWS_CERT_PRIVATE[] = "-----BEGIN RSA PRIVATE KEY-----\n" \
"MIIEpQIQEAphsi45x87olzmdBqAOrHfZCADpJvguBAAKCZQDmHuAsjyoXwRxu9Xw\n" \
"Ywi735aadERdTgZL84y5cgvgoBsi+tKbmi2Atu9XzQb956B7kf51X0goBGNO4oeA\n" \
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" \
"-----END RSA PRIVATE KEY-----\n";

// O certificado para o seu dispositivo
const char AWS_CERT_CRT[] = "-----BEGIN CERTIFICATE-----\n" \
"MIIDwWH8yD0aOIBAgIUPCdJZxYDQYJKoZIhvcVfWTCCAkGgA65JHHAIAQEPMYwNL\n" \
"BQAwAdlYiBTZX2aWN1hFtYXpJ1UECcyBPTTFLMem9uIFEkGwxCQWvbi5lPUjb20g\n" \
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" \
"-----END CERTIFICATE-----\n";

#endif

Observe que os certificados devem ser codificados de uma determinada maneira. Para fazer isso, eu os abri em Sublime Text e uma citação no início de cada linha e \n" \ no final de cada linha.

Não se esqueça de incluir o arquivo certs.h em seu esboço principal se você optou por colocá-los em um arquivo separado:

#include "certs.h"

 

Conectando-se ao AWS IOT (via MQTT)

Armados com os certificados, agora podemos nos conectar à AWS via MQTT! Vou começar definindo algumas variáveis de configuração no arquivo de sketch principal:

// O nome do dispositivo. Isso DEVE corresponder ao nome definido no console AWS
#define DEVICE_NAME "my-arduino-device"

// O endpoint MQTTT para o dispositivo (exclusivo para cada conta AWS, mas compartilhado entre os dispositivos dentro da conta)
#define AWS_IOT_ENDPOINT "xxxxxxxxxxx.iot.eu-west-1.amazonaws.com"

// O tópico MQTT em que este dispositivo deve publicar
#define AWS_IOT_TOPIC "$aws/things/" DEVICE_NAME "/shadow/update"

// Quantas vezes devemos tentar nos conectar ao AWS
#define AWS_MAX_RECONNECT_TRIES 50

Também precisamos instalar uma biblioteca MQTT. Existem muitos disponíveis para o Arduino, mas descobri que o do Joel Gaehwiler funciona melhor. Você pode instalá-lo através do Arduino Library Manager

Em seguida, podemos criar duas instâncias. WiFiClientSecure, que será responsável por lidar com a criptografia com nossos certificados e MQTTClient que realmente “falará” o protocolo MQTT com a AWS:

#include <WiFiClientSecure.h>
#include <MQTTClient.h>

WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient();

Nota lateral: por padrão, o tamanho máximo dos pacotes que você pode publicar e receber é definido como 128 bytes. Para aumentá-lo, você pode usar o seguinte:

MQTTClient client = MQTTClient(512);

Agora estamos prontos para estabelecer nossa conexão MQTT com a AWS! Mais uma vez, prefiro colocar essa lógica em uma função separada:

void connectToAWS()
{
    // Configure WiFiClientSecure para usar os certificados AWS que geramos
    net.setCACert(AWS_CERT_CA);
    net.setCertificate(AWS_CERT_CRT);
    net.setPrivateKey(AWS_CERT_PRIVATE);

    // Conecte-se ao corretor MQTT no endpoint AWS que definimos anteriormente
    client.begin(AWS_IOT_ENDPOINT, 8883, net);

    // Tente se conectar ao AWS e conte quantas vezes tentamos novamente.
    int retries = 0;
    Serial.print("Connecting to AWS IOT");

    while (!client.connect(DEVICE_NAME) && retries < AWS_MAX_RECONNECT_TRIES) {
        Serial.print(".");
        delay(100);
        retries++;
    }

    // Certifique-se de que realmente conectamos com sucesso ao broker MQTT
        // Do contrário, apenas encerramos a função e aguardamos o próximo loop.
    if(!client.connected()){
        Serial.println(" Tempo esgotado!");
        return;
    }

    // Se pousarmos aqui, nos conectamos com sucesso ao AWS!
        // E podemos assinar tópicos e enviar mensagens.
    Serial.println("Conectado!");
}

Neste ponto, devemos ter uma conexão aberta com a AWS pronta para enviar ou receber mensagens

 

Enviando JSON com ArduinoJson

A etapa final é usar nossa conexão MQTT para enviar alguns dados para a AWS! A maneira mais comum de fazer isso é atualizando o “Documento sombra” do dispositivo. Este é um recurso especial que a AWS projetou com dispositivos IoT em mente.

Resumindo, cada dispositivo possui dois estados: um relatado e um estado desejado. O estado relatado é usado pelo dispositivo para rastrear seu estado atual, enquanto o estado desejado é usado para alterar algo.

Um exemplo simples: imagine que você tem uma luz LED conectada ao AWS IoT. No momento, ele está desativado, então o estado relatado é definido como false. Quando você quiser ligá-lo, defina o estado desejado como true. A Amazon agora verá que os estados não correspondem e enviará uma mensagem para o dispositivo, avisando que deve ligar.

Agora vamos imaginar que estamos construindo uma estação meteorológica e que queremos usar o estado “relatado” para rastrear vários sensores. Precisamos construir o seguinte JSON e publicá-lo no tópico/shadow/update MQTT:

{
    "state": {
        "reported": {
            "temperature": 23.76,
            "humidity": 78.12,
            "wifi_strength": -87.27,
            "location": {
                "name": "Garden"
            }
        }
    }
}

Poderíamos, é claro, fazer esse documento JSON concatenando strings, mas não vamos fazer isso conosco. Em vez disso, vamos usar a biblioteca ArduinoJson. Você pode instalá-lo através do Library Manager conforme detalhado antes:

Com essa biblioteca, podemos recriar o documento JSON assim:

#include <ArduinoJson.h>

void sendJsonToAWS()
{
  StaticJsonDocument<512> jsonDoc;
  JsonObject stateObj = jsonDoc.createNestedObject("state");
  JsonObject reportedObj = stateObj.createNestedObject("reported");
  
  // Escreva a temperatura e umidade. Aqui você pode usar qualquer tipo C ++ (e pode se referir a variáveis)
  reportedObj["temperature"] = 23.76;
  reportedObj["humidity"] = 78.12;
  reportedObj["wifi_strength"] = WiFi.RSSI();
  
  // Cria um objeto aninhado "local"
  JsonObject locationObj = reportedObj.createNestedObject("location");
  locationObj["name"] = "Garden";

  // Publique a mensagem para AWS
  client.publish(AWS_IOT_TOPIC, jsonBuffer);
}

Algumas coisas interessantes estão acontecendo que você pode querer saber. Para começar, o tamanho do documento JSON é limitado pelo tamanho do StaticJsonDocument. Nesse caso, o buffer tem 512 bytes. Aumente ou diminua este valor conforme necessário (e lembre-se de que aumentar o tamanho do documento JSON, provavelmente significa que você também terá que aumentar o tamanho do buffer MQTT)

StaticJsonDocument<512> jsonDoc;

Adicionar campos ao objeto é muito simples, basta usar a notação de array. ArduinoJson examinará o tipo C ++ para descobrir como ele deve codificar seus dados em um documento JSON válido. Legal!

reportedObj["myVariableName"] = "MyValue";

Se você quiser ver o JSON resultante no console serial, pode usar o método serializeJson:

serializeJson(doc, Serial);

 

Função Loop/Setup

Foram realizadas! Criamos todas as funções de que precisamos para estabelecer uma conexão com a AWS, construir um objeto JSON e enviá-lo.

Esta é a aparência de suas funções de setup e loop:

void setup() {
  Serial.begin(9600);
  connectToWiFi();
  connectToAWS();
}

void loop() {
  sendJsonToAWS();
  client.loop();
  delay(1000);
}

Observe a linha client.loop(). Isso é essencial para manter a conexão MQTT com a AWS ativa.

 

Modelo inicial no GitHub

Foi publicado este exemplo como um kit inicial completo neste GitHub. Confira aqui:

Conclusão

À primeira vista, configurar e usar AWS IOT com um ESP32 parece muito trabalhoso. Você precisa criar certificados especiais, conectar-se ao WiFi, configurar uma conexão MQTT segura e construir uma mensagem JSON. No entanto, se você seguir passo a passo, as coisas não serão tão complicadas.

Espero que este tutorial tenha esclarecido tudo. Então, o que você está esperando? Vá construir algumas coisas interessantes de IoT com um ESP32 e AWS;) Também deixe-me saber nos comentários em quais dispositivos IoT você está trabalhando e como eles se beneficiarão de uma configuração como esta.