355 lines
11 KiB
C
355 lines
11 KiB
C
/*
|
|
* Copyright (c) 2017 Digi International Inc.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
* =======================================================================
|
|
*/
|
|
|
|
#include <aws_iot_config.h>
|
|
#include <aws_iot_log.h>
|
|
#include <aws_iot_shadow_interface.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
|
|
#include "aws_config.h"
|
|
#include "aws_control.h"
|
|
#include "device_control.h"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
D E F I N I T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200
|
|
|
|
/*------------------------------------------------------------------------------
|
|
F U N C T I O N D E C L A R A T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
static void led_actuate_callback(const char *json_string, uint32_t len,
|
|
jsonStruct_t *json_struct);
|
|
static void shadow_update_status_callback(const char *pThingName,
|
|
ShadowActions_t action,
|
|
Shadow_Ack_Status_t status,
|
|
const char *pReceivedJsonDocument,
|
|
void *pContextData);
|
|
static void disconnect_callback(AWS_IoT_Client *mqtt_client, void *data);
|
|
|
|
/*------------------------------------------------------------------------------
|
|
G L O B A L V A R I A B L E S
|
|
------------------------------------------------------------------------------*/
|
|
aws_iot_cfg_t aws_cfg;
|
|
device_shadow_t *device_shadow;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
F U N C T I O N D E F I N I T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
/*
|
|
* initialize_shadow_client() - Initialize AWS IoT Device SDK
|
|
*
|
|
* @mqtt_client: MQTT client.
|
|
* @aws_config: Absolute path of the configuration file to use.
|
|
* NULL to use the default one (/etc/awsiotsdk.conf).
|
|
*
|
|
* Return: An IoT error code defining successful/failed initialization.
|
|
*/
|
|
IoT_Error_t initialize_shadow_client(AWS_IoT_Client *mqtt_client, const char *config_file)
|
|
{
|
|
ShadowInitParameters_t shadow_init_params = ShadowInitParametersDefault;
|
|
IoT_Error_t rc = FAILURE;
|
|
char root_ca[PATH_MAX + 1];
|
|
char client_crt[PATH_MAX + 1];
|
|
char client_key[PATH_MAX + 1];
|
|
|
|
if (parse_configuration(config_file ? config_file : AWS_IOT_CONFIG_FILE, &aws_cfg) != 0)
|
|
return FAILURE;
|
|
|
|
snprintf(root_ca, PATH_MAX + 1, "%s/%s", aws_cfg.certs_path, aws_cfg.rootca_fname);
|
|
snprintf(client_crt, PATH_MAX + 1, "%s/%s", aws_cfg.certs_path, aws_cfg.signed_cert_fname);
|
|
snprintf(client_key, PATH_MAX + 1, "%s/%s", aws_cfg.certs_path, aws_cfg.priv_key_fname);
|
|
|
|
IOT_DEBUG("Root CA file: %s", root_ca);
|
|
IOT_DEBUG("Device signed certificate file: %s", client_crt);
|
|
IOT_DEBUG("Device private key file: %s", client_key);
|
|
|
|
/* Initialize the MQTT client */
|
|
shadow_init_params.pHost = aws_cfg.host;
|
|
shadow_init_params.port = aws_cfg.port;
|
|
shadow_init_params.pRootCA = root_ca;
|
|
shadow_init_params.pClientCRT = client_crt;
|
|
shadow_init_params.pClientKey = client_key;
|
|
shadow_init_params.enableAutoReconnect = false; /* Enable later */
|
|
|
|
IOT_INFO("Initializing MQTT...");
|
|
rc = aws_iot_shadow_init(mqtt_client, &shadow_init_params);
|
|
if (rc != SUCCESS)
|
|
return rc;
|
|
|
|
rc = aws_iot_mqtt_set_disconnect_handler(mqtt_client,
|
|
disconnect_callback, NULL);
|
|
if (rc != SUCCESS) {
|
|
IOT_ERROR("Unable to set MQTT disconnect handler, error: %d",
|
|
rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Enable Auto Reconnect functionality.
|
|
* Min and max 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(mqtt_client, true);
|
|
if (rc != SUCCESS)
|
|
IOT_ERROR("Unable to enable auto-reconnect, error: %d", rc);
|
|
|
|
/*
|
|
* Workaround for shadow updates getting out of sync:
|
|
*
|
|
* WARN: shadow_delta_callback L#504 Old Delta Message received -
|
|
* Ignoring rx: 40408 local: 40408
|
|
*
|
|
* See discussion here:
|
|
* https://github.com/aws/aws-iot-device-sdk-embedded-C/issues/32
|
|
*/
|
|
aws_iot_shadow_disable_discard_old_delta_msgs();
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* connect_shadow_client() - Establish MQTT connection
|
|
*
|
|
* @mqtt_client: MQTT client.
|
|
*
|
|
* Return: An IoT error code defining successful/failed connection.
|
|
*/
|
|
IoT_Error_t connect_shadow_client(AWS_IoT_Client *mqtt_client)
|
|
{
|
|
ShadowConnectParameters_t conn_params = ShadowConnectParametersDefault;
|
|
|
|
conn_params.pMyThingName = aws_cfg.thing_name;
|
|
conn_params.pMqttClientId = aws_cfg.client_id;
|
|
conn_params.mqttClientIdLen = (uint16_t) strlen(aws_cfg.client_id);
|
|
conn_params.deleteActionHandler = NULL;
|
|
|
|
IOT_INFO("Shadow connecting...");
|
|
return aws_iot_shadow_connect(mqtt_client, &conn_params);
|
|
}
|
|
|
|
/**
|
|
* disconnect_shadow_client() - Close MQTT connection
|
|
*
|
|
* @mqtt_client: MQTT client.
|
|
*
|
|
* Return: An IoT error code defining successful/failed disconnect status.
|
|
*/
|
|
IoT_Error_t disconnect_shadow_client(AWS_IoT_Client *mqtt_client)
|
|
{
|
|
IoT_Error_t rc = FAILURE;
|
|
|
|
IOT_INFO("Disconnecting...");
|
|
|
|
rc = aws_iot_shadow_disconnect(mqtt_client);
|
|
|
|
free_configuration();
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* initialize_shadow() - Initialize device Shadow
|
|
*
|
|
* @mqtt_client: MQTT client.
|
|
* @dev_shadow: Device shadow.
|
|
*
|
|
* Return: An IoT error code defining successful/failed initialization.
|
|
*/
|
|
IoT_Error_t initialize_shadow(AWS_IoT_Client *mqtt_client,
|
|
device_shadow_t *dev_shadow)
|
|
{
|
|
IoT_Error_t rc = FAILURE;
|
|
|
|
device_shadow = dev_shadow;
|
|
|
|
device_shadow->temp_handler.cb = NULL;
|
|
device_shadow->temp_handler.pKey = ATTR_TEMPERATURE;
|
|
device_shadow->temp_handler.pData = &(device_shadow->temp);
|
|
device_shadow->temp_handler.type = SHADOW_JSON_DOUBLE;
|
|
|
|
device_shadow->temp = 0;
|
|
device_shadow->temp_update = 0;
|
|
|
|
device_shadow->cpu_load_handler.cb = NULL;
|
|
device_shadow->cpu_load_handler.pKey = ATTR_CPU_LOAD;
|
|
device_shadow->cpu_load_handler.pData = &(device_shadow->cpu_load);
|
|
device_shadow->cpu_load_handler.type = SHADOW_JSON_DOUBLE;
|
|
|
|
device_shadow->cpu_load = 0;
|
|
device_shadow->cpu_load_update = 0;
|
|
|
|
device_shadow->led_actuator.cb = led_actuate_callback;
|
|
device_shadow->led_actuator.pKey = ATTR_LED;
|
|
device_shadow->led_actuator.pData = &(device_shadow->led_on);
|
|
device_shadow->led_actuator.type = SHADOW_JSON_BOOL;
|
|
|
|
device_shadow->led_on = false;
|
|
device_shadow->led_update = 0;
|
|
|
|
device_shadow->aws_config = &aws_cfg;
|
|
|
|
rc = aws_iot_shadow_register_delta(mqtt_client,
|
|
&(device_shadow->led_actuator));
|
|
if (rc != SUCCESS)
|
|
IOT_ERROR("Unable to register Shadow Delta for LED, error: %d", rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* update_shadow() - Update thing shadow.
|
|
*
|
|
* @mqtt_client: MQTT client.
|
|
*
|
|
* Return: An IoT error code defining successful/failed update action.
|
|
*/
|
|
IoT_Error_t update_shadow(AWS_IoT_Client *mqtt_client)
|
|
{
|
|
char json_doc_buf[MAX_LENGTH_OF_UPDATE_JSON_BUFFER];
|
|
size_t size_json_doc_buf = sizeof(json_doc_buf) / sizeof(json_doc_buf[0]);
|
|
IoT_Error_t rc = FAILURE;
|
|
|
|
rc = aws_iot_shadow_init_json_document(json_doc_buf, size_json_doc_buf);
|
|
if (rc != SUCCESS)
|
|
return rc;
|
|
|
|
rc = aws_iot_shadow_add_reported(json_doc_buf, size_json_doc_buf,
|
|
3,
|
|
&(device_shadow->temp_handler),
|
|
&(device_shadow->cpu_load_handler),
|
|
&(device_shadow->led_actuator));
|
|
if (rc != SUCCESS)
|
|
return rc;
|
|
|
|
rc = aws_iot_finalize_json_document(json_doc_buf, size_json_doc_buf);
|
|
if (rc != SUCCESS)
|
|
return rc;
|
|
|
|
return aws_iot_shadow_update(mqtt_client, aws_cfg.thing_name,
|
|
json_doc_buf,
|
|
shadow_update_status_callback,
|
|
(void *) device_shadow, 4, true);
|
|
}
|
|
|
|
/**
|
|
* led_actuate_callback() - Callback to be executed on receiving the LED value
|
|
*
|
|
* @json_string: Thing name of the shadow to be updated.
|
|
* @len: The response of the action.
|
|
* @json_struct: The struct used to parse JSON value.
|
|
*/
|
|
static void led_actuate_callback(const char *json_string, uint32_t len,
|
|
jsonStruct_t *json_struct)
|
|
{
|
|
bool status = false;
|
|
int led_gpio = aws_cfg.led_gpio;
|
|
IOT_UNUSED(json_string);
|
|
IOT_UNUSED(len);
|
|
|
|
if (json_struct == NULL)
|
|
return;
|
|
|
|
status = *(bool *) (json_struct->pData);
|
|
|
|
if (led_gpio <= -1 ||
|
|
(led_gpio > -1 && set_gpio_value(led_gpio, status) == 0)) {
|
|
IOT_INFO("Delta - LED state changed to %s", status ? ON : OFF);
|
|
device_shadow->led_update = 1;
|
|
} else {
|
|
IOT_ERROR("Error setting LED to %s", status ? ON : OFF);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* shadow_update_status_callback() - AWS IoT Device SDK update status callback
|
|
*
|
|
* Callback to inform of the response from the AWS IoT Shadow service.
|
|
*
|
|
* @pThingName: Thing name of the shadow to be updated.
|
|
* @action: The response of the action.
|
|
* @status: Update action status: accepted, rejected or time out.
|
|
* @pReceivedJsonDocument: Received JSON document.
|
|
* @pContextData: Data for the callback. NULL if not used.
|
|
*/
|
|
static void shadow_update_status_callback(const char *pThingName,
|
|
ShadowActions_t action,
|
|
Shadow_Ack_Status_t status,
|
|
const char *pReceivedJsonDocument,
|
|
void *pContextData)
|
|
{
|
|
device_shadow_t *dev_shadow = NULL;
|
|
|
|
IOT_UNUSED(pThingName);
|
|
IOT_UNUSED(pReceivedJsonDocument);
|
|
|
|
if (action != SHADOW_UPDATE)
|
|
return;
|
|
|
|
dev_shadow = (device_shadow_t *)pContextData;
|
|
|
|
switch(status) {
|
|
case SHADOW_ACK_TIMEOUT:
|
|
IOT_INFO("Shadow update timeout");
|
|
break;
|
|
case SHADOW_ACK_REJECTED:
|
|
IOT_INFO("Shadow update rejected");
|
|
break;
|
|
case SHADOW_ACK_ACCEPTED:
|
|
IOT_INFO("Shadow update accepted");
|
|
dev_shadow->temp_update = 0;
|
|
dev_shadow->cpu_load_update = 0;
|
|
dev_shadow->led_update = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* disconnect_callback() - AWS IoT Device SDK disconnect callback
|
|
*
|
|
* It is called whenever the client disconnects with error.
|
|
*
|
|
* @mqtt_client: MQTT client.
|
|
* @data: Reference to the data to be passed as argument when
|
|
* disconnect handler is called
|
|
*/
|
|
static void disconnect_callback(AWS_IoT_Client *mqtt_client, void *data)
|
|
{
|
|
IoT_Error_t rc = FAILURE;
|
|
|
|
IOT_WARN("MQTT Disconnect");
|
|
|
|
if (mqtt_client == NULL)
|
|
return;
|
|
|
|
IOT_UNUSED(data);
|
|
|
|
if (aws_iot_is_autoreconnect_enabled(mqtt_client)) {
|
|
IOT_INFO("Auto-reconnect enabled. Reconnecting attempt will start now");
|
|
} else {
|
|
IOT_WARN("Auto-reconnect not enabled. Starting manual reconnect...");
|
|
rc = aws_iot_mqtt_attempt_reconnect(mqtt_client);
|
|
if (rc == NETWORK_RECONNECTED) {
|
|
IOT_WARN("Manual Reconnect successful");
|
|
} else {
|
|
IOT_WARN("Manual Reconnect failed, error: %d", rc);
|
|
}
|
|
}
|
|
}
|