/* * 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 #include #include #include #include #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); } } }