AWS IoT Device SDK Support

Use device cert/key embedded in firmware, or loaded from filesystem.
This commit is contained in:
Angus Gratton
2016-10-05 16:32:09 +11:00
parent 74817c35f3
commit da660b234c
32 changed files with 2076 additions and 4 deletions
@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := aws_iot_thing_shadow
include $(IDF_PATH)/make/project.mk
@@ -0,0 +1,34 @@
# Amazon Web Services IoT Thing Shadow Example
This is an adaptation of the [AWS IoT C SDK](https://github.com/aws/aws-iot-device-sdk-embedded-C) "shadow_sample" example for ESP-IDF.
# Provisioning/Configuration
See the README.md in the parent directory for information about configuring the AWS IoT examples.
After following those steps, there is one additional step for this exmaple:
## Set Thing Name
For this example, you will need to set a Thing Name under `make menuconfig` -> `Example Configuration` -> `AWS IoT Thing Name`.
The Thing Name should match a Thing that you created while following the Getting Started guide (to check the Things you have registered, go t othe AWS IoT console web interface, click Registry and then click Things).
# Monitoring Thing Status
After flashing the example to your ESP32, it should connect to Amazon and start updating the example Thing's shadow.
In the ESP32's serial output, you should see the message "Update accepted" logged every couple of seconds.
You can monitor the Thing status from the AWS IoT console web interface:
* On the left-hand toolbar, click Registry and then click Things.
* Click on the "Thing" you set up for the example.
* Click on the "Shadow" sidebar link. You should see the Thing Shadow updating regularly.
* Click on the "Activity" sidebar link to see all Thing Shadow updates in a list. You can examine each update individually.
# Troubleshooting
If you're having problems with the AWS IoT connection itself, check the Troubleshooting section of the README in the parent directory.
* If your Thing is connecting and appears to be successfully updating, but you don't see any updates in the AWS IoT console, then check that the Thing Name in the Example Configuration under menuconfig matches exactly the thing name in AWS IoT console (including case).
@@ -0,0 +1,66 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "myssid"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
config AWS_EXAMPLE_CLIENT_ID
string "AWS IoT Client ID"
default "myesp32"
help
AWS IoT Client ID for the example. Should be unique for every device.
config AWS_EXAMPLE_THING_NAME
string "AWS IoT Thing Name"
default "myesp32"
help
AWS IoT Thing Name for the example. Should be unique for every device.
choice EXAMPLE_CERT_SOURCE
prompt "AWS IoT Certificate Source"
default EXAMPLE_EMBEDDED_CERTS
help
AWS IoT requires loading of a device-specific certificate and private key,
and a common Root CA Certificate. These can be compiled into the example
app, or they can be loaded via the filesystem from an SD card.
config EXAMPLE_EMBEDDED_CERTS
bool "Embed into app"
config EXAMPLE_SDCARD_CERTS
bool "Load from SD card"
select EXAMPLE_FILESYSTEM_CERTS
endchoice
# Currently this is equivalent to EXAMPLE_SDCARD_CERTS,
# however eventually we want to support more filesystem
# sources (SPIFFS, etc.) So this hidden config item
# is selected whenever the item should load from filesystem.
config EXAMPLE_FILESYSTEM_CERTS
bool
config EXAMPLE_CERTIFICATE_PATH
string "Device Certificate Path"
depends on EXAMPLE_FILESYSTEM_CERTS
default "/sdcard/certificate.pem.crt"
config EXAMPLE_PRIVATE_KEY_PATH
string "Device Private Key Path"
depends on EXAMPLE_FILESYSTEM_CERTS
default "/sdcard/private.pem.key"
config EXAMPLE_ROOT_CA_PATH
string "Root CA Certificate Path"
depends on EXAMPLE_FILESYSTEM_CERTS
default "/sdcard/aws-root-ca.pem"
endmenu
@@ -0,0 +1,3 @@
Copy certificate files for AWS IoT SDK example here
See README.md in main example directory for details.
@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----
@@ -0,0 +1,22 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
ifdef CONFIG_EXAMPLE_EMBEDDED_CERTS
# Certificate files. certificate.pem.crt & private.pem.key must be downloaded
# from AWS, see README for details.
COMPONENT_EMBED_TXTFILES := certs/aws-root-ca.pem certs/certificate.pem.crt certs/private.pem.key
ifndef IDF_CI_BUILD
# Print an error if the certificate/key files are missing
$(COMPONENT_PATH)/certs/certificate.pem.crt $(COMPONENT_PATH)/certs/private.pem.key:
@echo "Missing PEM file $@. This file identifies the ESP32 to AWS for the example, see README for details."
exit 1
else # IDF_CI_BUILD
# this case is for the internal Continuous Integration build which
# compiles all examples. Add some dummy certs so the example can
# compile (even though it won't work)
$(COMPONENT_PATH)/certs/certificate.pem.crt $(COMPONENT_PATH)/certs/private.pem.key:
echo "Dummy certificate data for continuous integration" > $@
endif
endif
@@ -0,0 +1,368 @@
/*
* Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD
*
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/**
* @file thing_shadow_sample.c
* @brief A simple connected window example demonstrating the use of Thing Shadow
*
* See example README for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "aws_iot_config.h"
#include "aws_iot_log.h"
#include "aws_iot_version.h"
#include "aws_iot_mqtt_client_interface.h"
#include "aws_iot_shadow_interface.h"
/*!
* The goal of this sample application is to demonstrate the capabilities of shadow.
* This device(say Connected Window) will open the window of a room based on temperature
* It can report to the Shadow the following parameters:
* 1. temperature of the room (double)
* 2. status of the window (open or close)
* It can act on commands from the cloud. In this case it will open or close the window based on the json object "windowOpen" data[open/close]
*
* The two variables from a device's perspective are double temperature and bool windowOpen
* The device needs to act on only on windowOpen variable, so we will create a primitiveJson_t object with callback
The Json Document in the cloud will be
{
"reported": {
"temperature": 0,
"windowOpen": false
},
"desired": {
"windowOpen": false
}
}
*/
static const char *TAG = "shadow";
#define ROOMTEMPERATURE_UPPERLIMIT 32.0f
#define ROOMTEMPERATURE_LOWERLIMIT 25.0f
#define STARTING_ROOMTEMPERATURE ROOMTEMPERATURE_LOWERLIMIT
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200
/* The examples use simple WiFi configuration that you can set via
'make menuconfig'.
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
/* CA Root certificate, device ("Thing") certificate and device
* ("Thing") key.
Example can be configured one of two ways:
"Embedded Certs" are loaded from files in "certs/" and embedded into the app binary.
"Filesystem Certs" are loaded from the filesystem (SD card, etc.)
See example README for more details.
*/
#if defined(CONFIG_EXAMPLE_EMBEDDED_CERTS)
extern const uint8_t aws_root_ca_pem_start[] asm("_binary_aws_root_ca_pem_start");
extern const uint8_t aws_root_ca_pem_end[] asm("_binary_aws_root_ca_pem_end");
extern const uint8_t certificate_pem_crt_start[] asm("_binary_certificate_pem_crt_start");
extern const uint8_t certificate_pem_crt_end[] asm("_binary_certificate_pem_crt_end");
extern const uint8_t private_pem_key_start[] asm("_binary_private_pem_key_start");
extern const uint8_t private_pem_key_end[] asm("_binary_private_pem_key_end");
#elif defined(CONFIG_EXAMPLE_FILESYSTEM_CERTS)
static const char * DEVICE_CERTIFICATE_PATH = CONFIG_EXAMPLE_CERTIFICATE_PATH;
static const char * DEVICE_PRIVATE_KEY_PATH = CONFIG_EXAMPLE_PRIVATE_KEY_PATH;
static const char * ROOT_CA_PATH = CONFIG_EXAMPLE_ROOT_CA_PATH;
#else
#error "Invalid method for loading certs"
#endif
/**
* @brief Default MQTT HOST URL is pulled from the aws_iot_config.h which
* uses menuconfig to find a default.
*/
char HostAddress[255] = AWS_IOT_MQTT_HOST;
/**
* @brief Default MQTT port is pulled from the aws_iot_config.h which
* uses menuconfig to find a default.
*/
uint32_t port = AWS_IOT_MQTT_PORT;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
static void simulateRoomTemperature(float *pRoomTemperature) {
static float deltaChange;
if(*pRoomTemperature >= ROOMTEMPERATURE_UPPERLIMIT) {
deltaChange = -0.5f;
} else if(*pRoomTemperature <= ROOMTEMPERATURE_LOWERLIMIT) {
deltaChange = 0.5f;
}
*pRoomTemperature += deltaChange;
}
static bool shadowUpdateInProgress;
void ShadowUpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status,
const char *pReceivedJsonDocument, void *pContextData) {
IOT_UNUSED(pThingName);
IOT_UNUSED(action);
IOT_UNUSED(pReceivedJsonDocument);
IOT_UNUSED(pContextData);
shadowUpdateInProgress = false;
if(SHADOW_ACK_TIMEOUT == status) {
ESP_LOGE(TAG, "Update timed out");
} else if(SHADOW_ACK_REJECTED == status) {
ESP_LOGE(TAG, "Update rejected");
} else if(SHADOW_ACK_ACCEPTED == status) {
ESP_LOGI(TAG, "Update accepted");
}
}
void windowActuate_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) {
IOT_UNUSED(pJsonString);
IOT_UNUSED(JsonStringDataLen);
if(pContext != NULL) {
ESP_LOGI(TAG, "Delta - Window state changed to %d", *(bool *) (pContext->pData));
}
}
void aws_iot_task(void *param) {
IoT_Error_t rc = FAILURE;
char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER];
size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]);
float temperature = 0.0;
bool windowOpen = false;
jsonStruct_t windowActuator;
windowActuator.cb = windowActuate_Callback;
windowActuator.pData = &windowOpen;
windowActuator.pKey = "windowOpen";
windowActuator.type = SHADOW_JSON_BOOL;
jsonStruct_t temperatureHandler;
temperatureHandler.cb = NULL;
temperatureHandler.pKey = "temperature";
temperatureHandler.pData = &temperature;
temperatureHandler.type = SHADOW_JSON_FLOAT;
ESP_LOGI(TAG, "AWS IoT SDK Version %d.%d.%d-%s", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG);
// initialize the mqtt client
AWS_IoT_Client mqttClient;
ShadowInitParameters_t sp = ShadowInitParametersDefault;
sp.pHost = AWS_IOT_MQTT_HOST;
sp.port = AWS_IOT_MQTT_PORT;
#if defined(CONFIG_EXAMPLE_EMBEDDED_CERTS)
sp.pClientCRT = (const char *)certificate_pem_crt_start;
sp.pClientKey = (const char *)private_pem_key_start;
sp.pRootCA = (const char *)aws_root_ca_pem_start;
#elif defined(CONFIG_EXAMPLE_FILESYSTEM_CERTS)
sp.pClientCRT = DEVICE_CERTIFICATE_PATH;
sp.pClientKey = DEVICE_PRIVATE_KEY_PATH;
sp.pRootCA = ROOT_CA_PATH;
#endif
sp.enableAutoReconnect = false;
sp.disconnectHandler = NULL;
#ifdef CONFIG_EXAMPLE_SDCARD_CERTS
ESP_LOGI(TAG, "Mounting SD card...");
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 3,
};
sdmmc_card_t* card;
esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount SD card VFAT filesystem.");
abort();
}
#endif
/* Wait for WiFI to show as connected */
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Shadow Init");
rc = aws_iot_shadow_init(&mqttClient, &sp);
if(SUCCESS != rc) {
ESP_LOGE(TAG, "aws_iot_shadow_init returned error %d, aborting...", rc);
abort();
}
ShadowConnectParameters_t scp = ShadowConnectParametersDefault;
scp.pMyThingName = CONFIG_AWS_EXAMPLE_THING_NAME;
scp.pMqttClientId = CONFIG_AWS_EXAMPLE_CLIENT_ID;
scp.mqttClientIdLen = (uint16_t) strlen(CONFIG_AWS_EXAMPLE_CLIENT_ID);
ESP_LOGI(TAG, "Shadow Connect");
rc = aws_iot_shadow_connect(&mqttClient, &scp);
if(SUCCESS != rc) {
ESP_LOGE(TAG, "aws_iot_shadow_connect returned error %d, aborting...", rc);
abort();
}
/*
* Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h
* #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL
* #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL
*/
rc = aws_iot_shadow_set_autoreconnect_status(&mqttClient, true);
if(SUCCESS != rc) {
ESP_LOGE(TAG, "Unable to set Auto Reconnect to true - %d, aborting...", rc);
abort();
}
rc = aws_iot_shadow_register_delta(&mqttClient, &windowActuator);
if(SUCCESS != rc) {
ESP_LOGE(TAG, "Shadow Register Delta Error");
}
temperature = STARTING_ROOMTEMPERATURE;
// loop and publish a change in temperature
while(NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) {
rc = aws_iot_shadow_yield(&mqttClient, 200);
if(NETWORK_ATTEMPTING_RECONNECT == rc || shadowUpdateInProgress) {
rc = aws_iot_shadow_yield(&mqttClient, 1000);
// If the client is attempting to reconnect, or already waiting on a shadow update,
// we will skip the rest of the loop.
continue;
}
ESP_LOGI(TAG, "=======================================================================================");
ESP_LOGI(TAG, "On Device: window state %s", windowOpen ? "true" : "false");
simulateRoomTemperature(&temperature);
rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
if(SUCCESS == rc) {
rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 2, &temperatureHandler,
&windowActuator);
if(SUCCESS == rc) {
rc = aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
if(SUCCESS == rc) {
ESP_LOGI(TAG, "Update Shadow: %s", JsonDocumentBuffer);
rc = aws_iot_shadow_update(&mqttClient, CONFIG_AWS_EXAMPLE_THING_NAME, JsonDocumentBuffer,
ShadowUpdateStatusCallback, NULL, 4, true);
shadowUpdateInProgress = true;
}
}
}
ESP_LOGI(TAG, "*****************************************************************************************");
vTaskDelay(1000 / portTICK_RATE_MS);
}
if(SUCCESS != rc) {
ESP_LOGE(TAG, "An error occurred in the loop %d", rc);
}
ESP_LOGI(TAG, "Disconnecting");
rc = aws_iot_shadow_disconnect(&mqttClient);
if(SUCCESS != rc) {
ESP_LOGE(TAG, "Disconnect error %d", rc);
}
vTaskDelete(NULL);
}
static void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_WIFI_SSID,
.password = EXAMPLE_WIFI_PASS,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
void app_main()
{
initialise_wifi();
/* Temporarily pin task to core, due to FPU uncertainty */
xTaskCreatePinnedToCore(&aws_iot_task, "aws_iot_task", 16384+1024, NULL, 5, NULL, 1);
}
@@ -0,0 +1,8 @@
# Enable AWS IoT SDK support
CONFIG_AWS_IOT_SDK=y
# Enable FATFS read only with long filename support
# for loading Cert/CA/etc from filesystem
# (if enabled in config)
CONFIG_FATFS_CODEPAGE_437=y
CONFIG_FATFS_LFN_HEAP=y